<!doctype html>
<html lang="zh-CN">
<head>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    
    <meta name="referrer" content="no-referrer-when-downgrade">
    

    <title>Kotlin极速入门 | 早起的年轻人</title>
    <meta property="og:title" content="Kotlin极速入门 - 早起的年轻人">
    <meta property="og:type" content="article">
        
    <meta property="article:published_time" content='2021-08-26T21:55:08&#43;08:00'>
        
        
    <meta property="article:modified_time" content='2021-08-26T21:55:08&#43;08:00'>
        
    <meta name="Keywords" content="Flutter，golang,go语言,go语言笔记,飞雪无情,java,android,博客,项目管理,python,软件架构,公众号,小程序">
    <meta name="description" content="Kotlin极速入门">
        
    <meta name="author" content="luckly">
    <meta property="og:url" content="https://luckly.work/post/kotlin/kotlin%E6%9E%81%E9%80%9F%E5%85%A5%E9%97%A8/">
    <link rel="shortcut icon" href='/favicon.ico'  type="image/x-icon">

    <link rel="stylesheet" href='/css/normalize.css'>
    <link rel="stylesheet" href='/css/style.css'>
    <script type="text/javascript" src="//cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

    
    
    
        <link href="https://cdn.bootcdn.net/ajax/libs/fancybox/3.5.7/jquery.fancybox.min.css" rel="stylesheet">
    
    
    
    
        <link rel="stylesheet" href='/css/douban.css'>
    
        <link rel="stylesheet" href='/css/other.css'>
    
</head>

<link rel="stylesheet"
      href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.2.0/styles/default.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.2.0/highlight.min.js"></script>
<body>
    <header id="header" class="clearfix">
    <div class="container">
        <div class="col-group">
            <div class="site-name ">
                
                    <a id="logo" href="https://luckly.work/">
                        早起的年轻人
                    </a>
                
                <p class="description">专注于Flutter、Android、Java、Go语言(golang)、移动互联网、项目管理、软件架构</p>
            </div>
            <div>
                <nav id="nav-menu" class="clearfix">
                    <a class="current" href="https://luckly.work/">首页</a>
                    
                    <a  href="https://luckly.work/categories/" title="分类">分类</a>
                    
                    <a  href="https://luckly.work/tags/" title="标签">标签</a>
                    
                    <a  href="https://luckly.work/archives/" title="归档">归档</a>
                    
                    <a  href="https://luckly.work/about/" title="关于我">关于我</a>
                    
                    <a  href="https://github.com/ITmxs/" title="github">github</a>
                    
                </nav>
            </div>
        </div>
    </div>
</header>

    <div id="body">
        <div class="container">
            <div class="col-group">

                <div class="col-8" id="main">
                    
<div class="res-cons">
    <style type="text/css">
    .post-toc {
        position: fixed;
        width: 200px;
        margin-left: -210px;
        padding: 5px 10px;
        font-family: Athelas, STHeiti, Microsoft Yahei, serif;
        font-size: 12px;
        border: 1px solid rgba(0, 0, 0, .07);
        border-radius: 5px;
        background-color: rgba(255, 255, 255, 0.98);
        background-clip: padding-box;
        -webkit-box-shadow: 1px 1px 2px rgba(0, 0, 0, .125);
        box-shadow: 1px 1px 2px rgba(0, 0, 0, .125);
        word-wrap: break-word;
        white-space: nowrap;
        -webkit-box-sizing: border-box;
        box-sizing: border-box;
        z-index: 999;
        cursor: pointer;
        max-height: 70%;
        overflow-y: auto;
        overflow-x: hidden;
    }

    .post-toc .post-toc-title {
        width: 100%;
        margin: 0 auto;
        font-size: 20px;
        font-weight: 400;
        text-transform: uppercase;
        text-align: center;
    }

    .post-toc .post-toc-content {
        font-size: 15px;
    }

    .post-toc .post-toc-content>nav>ul {
        margin: 10px 0;
    }

    .post-toc .post-toc-content ul {
        padding-left: 20px;
        list-style: square;
        margin: 0.5em;
        line-height: 1.8em;
    }

    .post-toc .post-toc-content ul ul {
        padding-left: 15px;
        display: none;
    }

    @media print,
    screen and (max-width:1057px) {
        .post-toc {
            display: none;
        }
    }
</style>
<div class="post-toc" style="position: absolute; top: 188px;">
    <h2 class="post-toc-title">文章目录</h2>
    <div class="post-toc-content">
        <nav id="TableOfContents">
  <ul>
    <li><a href="#kotlin学习资料">Kotlin学习资料</a></li>
    <li><a href="#你需要知道一些比较重要的事">你需要知道一些比较重要的事</a></li>
  </ul>

  <ul>
    <li><a href="#为什么选择-kotlin">为什么选择 Kotlin？</a></li>
  </ul>

  <ul>
    <li><a href="#安装-kotlin-插件">安装 Kotlin 插件</a></li>
    <li><a href="#创建新工程">创建新工程</a></li>
    <li><a href="#将-java-代码转换为-kotlin">将 Java 代码转换为 Kotlin</a>
      <ul>
        <li><a href="#工程中配置-kotlin">工程中配置 Kotlin</a></li>
      </ul>
    </li>
  </ul>

  <ul>
    <li><a href="#包声明">包声明</a>
      <ul>
        <li><a href="#默认导入">默认导入</a></li>
      </ul>
    </li>
    <li><a href="#函数定义">函数定义</a>
      <ul>
        <li><a href="#可变长参数函数">可变长参数函数</a></li>
        <li><a href="#lambda匿名函数">lambda(匿名函数)</a></li>
      </ul>
    </li>
    <li><a href="#定义常量与变量">定义常量与变量</a></li>
    <li><a href="#注释">注释</a></li>
    <li><a href="#字符串模板">字符串模板</a></li>
    <li><a href="#null检查机制">NULL检查机制</a></li>
    <li><a href="#类型检测及自动类型转换">类型检测及自动类型转换</a></li>
    <li><a href="#区间">区间</a>
      <ul>
        <li><a href="#实例测试">实例测试</a></li>
      </ul>
    </li>
  </ul>

  <ul>
    <li><a href="#字面常量">字面常量</a></li>
    <li><a href="#比较两个数字">比较两个数字</a></li>
    <li><a href="#类型转换">类型转换</a></li>
    <li><a href="#位操作符">位操作符</a></li>
    <li><a href="#字符">字符</a></li>
    <li><a href="#布尔">布尔</a></li>
    <li><a href="#数组">数组</a></li>
    <li><a href="#字符串">字符串</a></li>
    <li><a href="#字符串模板-1">字符串模板</a></li>
  </ul>

  <ul>
    <li><a href="#if-表达式">IF 表达式</a>
      <ul>
        <li><a href="#实例">实例</a></li>
        <li><a href="#使用区间">使用区间</a></li>
      </ul>
    </li>
    <li><a href="#when-表达式">When 表达式</a>
      <ul>
        <li><a href="#实例-1">实例</a></li>
      </ul>
    </li>
  </ul>

  <ul>
    <li><a href="#for-循环">For 循环</a>
      <ul>
        <li><a href="#实例-2">实例</a></li>
      </ul>
    </li>
    <li><a href="#while-与-dowhile-循环">while 与 do&hellip;while 循环</a>
      <ul>
        <li><a href="#实例-3">实例</a></li>
      </ul>
    </li>
    <li><a href="#返回和跳转">返回和跳转</a>
      <ul>
        <li><a href="#break-和-continue-标签">Break 和 Continue 标签</a></li>
        <li><a href="#标签处返回">标签处返回</a></li>
      </ul>
    </li>
  </ul>

  <ul>
    <li><a href="#类定义">类定义</a></li>
    <li><a href="#类的属性">类的属性</a>
      <ul>
        <li><a href="#属性定义">属性定义</a></li>
        <li><a href="#getter-和-setter">getter 和 setter</a></li>
        <li><a href="#实例-4">实例</a></li>
      </ul>
    </li>
    <li><a href="#主构造器">主构造器</a>
      <ul>
        <li><a href="#实例-5">实例</a></li>
      </ul>
    </li>
    <li><a href="#次构造函数">次构造函数</a>
      <ul>
        <li><a href="#实例-6">实例</a></li>
      </ul>
    </li>
    <li><a href="#抽象类">抽象类</a></li>
    <li><a href="#嵌套类">嵌套类</a></li>
    <li><a href="#内部类">内部类</a></li>
    <li><a href="#匿名内部类">匿名内部类</a></li>
    <li><a href="#类的修饰符">类的修饰符</a>
      <ul>
        <li><a href="#实例-7">实例</a></li>
      </ul>
    </li>
  </ul>

  <ul>
    <li><a href="#构造函数">构造函数</a>
      <ul>
        <li><a href="#子类有主构造函数">子类有主构造函数</a></li>
        <li><a href="#子类没有主构造函数">子类没有主构造函数</a></li>
        <li><a href="#实例-8">实例</a></li>
      </ul>
    </li>
    <li><a href="#重写">重写</a></li>
    <li><a href="#属性重写">属性重写</a></li>
    <li><a href="#几点补充"><strong>几点补充：</strong></a></li>
  </ul>

  <ul>
    <li>
      <ul>
        <li><a href="#实现接口">实现接口</a></li>
      </ul>
    </li>
    <li><a href="#实例-9">实例</a>
      <ul>
        <li><a href="#接口中的属性">接口中的属性</a></li>
      </ul>
    </li>
    <li><a href="#实例-10">实例</a></li>
  </ul>

  <ul>
    <li><a href="#扩展函数">扩展函数</a></li>
    <li><a href="#扩展函数是静态解析的">扩展函数是静态解析的</a>
      <ul>
        <li><a href="#扩展一个空对象">扩展一个空对象</a></li>
      </ul>
    </li>
    <li><a href="#伴生对象的扩展">伴生对象的扩展</a></li>
    <li><a href="#扩展的作用域">扩展的作用域</a></li>
    <li><a href="#扩展声明为成员">扩展声明为成员</a></li>
  </ul>

  <ul>
    <li><a href="#数据类">数据类</a>
      <ul>
        <li><a href="#复制">复制</a></li>
        <li><a href="#实例-11">实例</a></li>
        <li><a href="#数据类以及解构声明">数据类以及解构声明</a></li>
        <li><a href="#标准数据类">标准数据类</a></li>
      </ul>
    </li>
    <li><a href="#密封类">密封类</a></li>
    <li><a href="#笔记">笔记</a></li>
  </ul>

  <ul>
    <li><a href="#泛型约束">泛型约束</a></li>
    <li><a href="#型变">型变</a>
      <ul>
        <li><a href="#声明处型变">声明处型变</a></li>
      </ul>
    </li>
    <li><a href="#星号投射">星号投射</a></li>
  </ul>

  <ul>
    <li>
      <ul>
        <li><a href="#枚举初始化">枚举初始化</a></li>
      </ul>
    </li>
    <li><a href="#使用枚举常量">使用枚举常量</a>
      <ul>
        <li><a href="#实例-12">实例</a></li>
      </ul>
    </li>
  </ul>

  <ul>
    <li><a href="#对象表达式">对象表达式</a></li>
    <li><a href="#对象声明">对象声明</a>
      <ul>
        <li><a href="#实例-13">实例</a></li>
      </ul>
    </li>
    <li><a href="#伴生对象">伴生对象</a></li>
    <li><a href="#对象表达式和对象声明之间的语义差异">对象表达式和对象声明之间的语义差异</a></li>
  </ul>

  <ul>
    <li><a href="#类委托">类委托</a></li>
    <li><a href="#属性委托">属性委托</a>
      <ul>
        <li><a href="#定义一个被委托的类">定义一个被委托的类</a></li>
      </ul>
    </li>
    <li><a href="#标准委托">标准委托</a>
      <ul>
        <li><a href="#延迟属性-lazy">延迟属性 Lazy</a></li>
      </ul>
    </li>
    <li><a href="#可观察属性-observable">可观察属性 Observable</a></li>
    <li><a href="#把属性储存在映射中">把属性储存在映射中</a></li>
    <li><a href="#not-null">Not Null</a></li>
    <li><a href="#局部委托属性">局部委托属性</a></li>
    <li><a href="#属性委托要求">属性委托要求</a></li>
    <li><a href="#翻译规则">翻译规则</a></li>
    <li><a href="#提供委托">提供委托</a></li>
  </ul>
</nav>
    </div>
</div>
<script type="text/javascript">
    $(document).ready(function () {
        var postToc = $(".post-toc");
        if (postToc.length) {
            var leftPos = $("#main").offset().left;
            if(leftPos<220){
                postToc.css({"width":leftPos-10,"margin-left":(0-leftPos)})
            }

            var t = postToc.offset().top - 20,
                a = {
                    start: {
                        position: "absolute",
                        top: t
                    },
                    process: {
                        position: "fixed",
                        top: 20
                    },
                };
            $(window).scroll(function () {
                var e = $(window).scrollTop();
                e < t ? postToc.css(a.start) : postToc.css(a.process)
            })
        }
    })
</script>
    <article class="post">
        <header>
            <h1 class="post-title">Kotlin极速入门</h1>
        </header>
        <date class="post-meta meta-date">
            2021年8月26日
        </date>
        
        <div class="post-meta">
            <span>|</span>
            
            <span class="meta-category"><a href='/categories/Kotlin'>Kotlin</a></span>
            
        </div>
        
        
        <div class="post-meta">
            <span id="busuanzi_container_page_pv">|<span id="busuanzi_value_page_pv"></span><span>
                    阅读</span></span>
        </div>
        
        
        <div class="post-content">
            <blockquote>
<p>本文主要介绍Kotlin语言的基础</p>
</blockquote>
<h1 id="关于kotlin">关于Kotlin</h1>
<p><a href="https://www.jianshu.com/p/1c83bd577cbb">https://www.jianshu.com/p/1c83bd577cbb</a></p>
<p>Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言，被称之为 Android 世界的Swift，由 JetBrains 设计开发并开源。</p>
<p>Kotlin 可以编译成Java字节码，也可以编译成 JavaScript，方便在没有 JVM 的设备上运行。</p>
<p>在Google I/O 2017中，Google 宣布 Kotlin 成为 Android 官方开发语言。</p>
<p><a href="https://kotlinlang.org/">官网</a></p>
<p><a href="https://www.kotlincn.net/">中文站</a></p>
<p>Kotlin为什么可以编写Android程序？</p>
<p>因为使用Java语言编写的代码最终需要编译成.class文件才能执行，而使用Kotlin编写的代码最终也会编译成.class文件。对于Android操作系统而言，它无须关心也无法关心程序是用什么语言开发的，只要最终执行的是一些合法的.class文件即可。</p>
<p>我们为什么要使用Kotlin开发Android程序？
1、每行代码行尾毫无意义的分号。
2、switch语句只支持int条件(java1.8版本后也支持String了)，且case最后要加break。
3、不是全面向对象语言，而是半面向对象语言。
4、不支持字符串内嵌表达式，拼接字符串繁杂。</p>
<h2 id="kotlin学习资料">Kotlin学习资料</h2>
<blockquote>
<p><a href="https://huanglizhuo.gitbooks.io/kotlin-in-chinese/">Kotlin-in-chinese</a>
<a href="https://www.jianshu.com/p/cc65e88d86ce">Kotlin系列之let、with、run、apply、also函数的使用</a>
<a href="https://blog.csdn.net/liyi1009365545/article/details/84064326">kotlin学习笔记：object关键字介绍与java中的静态变量与静态方法的实现以及@JvmField和@JvmStatic的使用</a>
<a href="https://blog.csdn.net/xlh1191860939/article/details/82109086">Kotlin学习系列之：可变参数</a></p>
<p><a href="http://uprogrammer.cn/kotlin-for-android-developers-zh/">《Kotlin for android developers》中文版翻译</a></p>
<p><a href="https://developer.android.google.cn/kotlin/first?hl=zh-cn">https://developer.android.google.cn/kotlin/first?hl=zh-cn</a></p>
</blockquote>
<h2 id="你需要知道一些比较重要的事">你需要知道一些比较重要的事</h2>
<ul>
<li><a href="https://blog.mindorks.com/learn-kotlin-lateinit-vs-lazy">Learn Kotlin - lateinit vs lazy</a></li>
<li><a href="https://blog.mindorks.com/learn-kotlin-apply-vs-with">Learn Kotlin - apply vs with</a></li>
<li><a href="https://blog.mindorks.com/learn-kotlin-data-class">Learn Kotlin - Data Class</a></li>
<li><a href="https://blog.mindorks.com/learn-kotlin-destructuring-declarations">Learn Kotlin - Destructuring Declarations</a></li>
<li><a href="https://blog.mindorks.com/learn-kotlin-extension-functions">Learn Kotlin - Extension Functions</a></li>
<li><a href="https://blog.mindorks.com/learn-kotlin-sealed-classes">Learn Kotlin - Sealed Classes</a></li>
</ul>
<h1 id="我的第一个-kotlin-程序">我的第一个 Kotlin 程序</h1>
<p>Kotlin 程序文件以 <strong>.kt</strong> 结尾，如：hello.kt 、app.kt。</p>
<p>以下测试demo均在</p>
<p><a href="https://play.kotlinlang.org/">kotlin在线工具编写 测试</a></p>
<p>我们将从helloword开始编写</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">package</span> hello                      <span style="color:#75715e">//  可选的包头
</span><span style="color:#75715e"></span> 
<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {    <span style="color:#75715e">// 包级可见的函数，接受一个字符串数组作为参数
</span><span style="color:#75715e"></span>   println(<span style="color:#e6db74">&#34;Hello World!&#34;</span>)         <span style="color:#75715e">// 分号可以省略
</span><span style="color:#75715e"></span>}
</code></pre></div><p>面向对象</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Greeter</span>(<span style="color:#66d9ef">val</span> name: String) {
   <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">greet</span>() { 
      println(<span style="color:#e6db74">&#34;Hello, </span><span style="color:#e6db74">$name</span><span style="color:#e6db74">&#34;</span>)
   }
}
 
<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
   Greeter(<span style="color:#e6db74">&#34;World!&#34;</span>).greet()          <span style="color:#75715e">// 创建一个对象不用 new 关键字
</span><span style="color:#75715e"></span>}
</code></pre></div><h2 id="为什么选择-kotlin">为什么选择 Kotlin？</h2>
<ul>
<li>简洁: 大大减少样板代码的数量。</li>
<li>安全: 避免空指针异常等整个类的错误。</li>
<li>互操作性: 充分利用 JVM、Android 和浏览器的现有库。</li>
<li>工具友好: 可用任何 Java IDE 或者使用命令行构建。</li>
</ul>
<p>由于我学kotiln主要是为了Android做基础</p>
<p>所以我就主要介绍Kotlin Android 的环境搭建</p>
<h1 id="kotlin-android-环境搭建">Kotlin Android 环境搭建</h1>
<h2 id="安装-kotlin-插件">安装 Kotlin 插件</h2>
<p>Android Studio 从 3.0(preview)版本开始将内置安装 Kotlin 插件。</p>
<p>打开 Settings ( Mac 为 Preferences) 面板，在右侧找到 Plugins 选项 (快捷键 Ctrl+, Mac 下为 command+)，搜索框输入 &ldquo;Kotlin&rdquo; 查找，点击 Search in repositories(仓库中搜索)，然后安装即可，安装完成之后需要重启 Android Studio。</p>
<p>
        <a data-fancybox="gallery" href="https://luckly007.oss-cn-beijing.aliyuncs.com/img/1495852874-4548-qiddj00bpde497.png">
            <img class="mx-auto" alt="img" src="https://luckly007.oss-cn-beijing.aliyuncs.com/img/1495852874-4548-qiddj00bpde497.png" />
        </a>
    </p>
<p>
        <a data-fancybox="gallery" href="https://luckly007.oss-cn-beijing.aliyuncs.com/img/image-20210826220248511.png">
            <img class="mx-auto" alt="image-20210826220248511" src="https://luckly007.oss-cn-beijing.aliyuncs.com/img/image-20210826220248511.png" />
        </a>
    </p>
<h2 id="创建新工程">创建新工程</h2>
<p>选择 Start a new Android Studio project 或者 File | New project，大多数选项均有默认值 ，只需要按几次&quot;回车&quot;键即可。</p>
<p>
        <a data-fancybox="gallery" href="https://luckly007.oss-cn-beijing.aliyuncs.com/img/image-20210826220713278.png">
            <img class="mx-auto" alt="image-20210826220713278" src="https://luckly007.oss-cn-beijing.aliyuncs.com/img/image-20210826220713278.png" />
        </a>
    </p>
<p>Android Studio 3.0 在当前对话框中提供启用 Kotlin 支持的选项，勾选后可以跳过 &ldquo;配置 Kotlin 工程(Configuring Kotlin in the project)&ldquo;的步骤。</p>
<p>选择 Android 版本:</p>
<p>
        <a data-fancybox="gallery" href="https://luckly007.oss-cn-beijing.aliyuncs.com/img/1495853670-8308-1-create-new-project.png">
            <img class="mx-auto" alt="img" src="https://luckly007.oss-cn-beijing.aliyuncs.com/img/1495853670-8308-1-create-new-project.png" />
        </a>
    </p>
<p>选择需要创建的 Activity 样式:</p>
<p>
        <a data-fancybox="gallery" href="https://luckly007.oss-cn-beijing.aliyuncs.com/img/1495853838-1520-0-create-new-project.png">
            <img class="mx-auto" alt="img" src="https://luckly007.oss-cn-beijing.aliyuncs.com/img/1495853838-1520-0-create-new-project.png" />
        </a>
    </p>
<p>命名该 Activity:</p>
<p>
        <a data-fancybox="gallery" href="https://luckly007.oss-cn-beijing.aliyuncs.com/img/1495853838-8955-1-create-new-project.png">
            <img class="mx-auto" alt="img" src="https://luckly007.oss-cn-beijing.aliyuncs.com/img/1495853838-8955-1-create-new-project.png" />
        </a>
    </p>
<p>在 Android Studio 3.0 中，可以选择使用 Kotlin 创建 activity，因此也不需要&quot;将Java 代码转换为 Kotlin(Converting Java code to Kotlin)&ldquo;这一步骤。</p>
<p>早期版本中则会先使用 Java 创建 activity，然后再使用自动转换工具 进行转换。</p>
<hr>
<h2 id="将-java-代码转换为-kotlin">将 Java 代码转换为 Kotlin</h2>
<p>重新打开Android Studio，新建一个Android项目吧，添加一个默认的MainActivity</p>
<p>打开 MainActivity.java 文件，通过菜单栏依次调出 Code | Convert Java File to Kotlin File：</p>
<p>
        <a data-fancybox="gallery" href="https://luckly007.oss-cn-beijing.aliyuncs.com/img/1495854751-7389-convert-java-to-kotlin.png">
            <img class="mx-auto" alt="img" src="https://luckly007.oss-cn-beijing.aliyuncs.com/img/1495854751-7389-convert-java-to-kotlin.png" />
        </a>
    </p>
<p>转换完成后即可看到使用 Kotlin 编写的 activity。</p>
<p>
        <a data-fancybox="gallery" href="https://luckly007.oss-cn-beijing.aliyuncs.com/img/1495854753-3864-converted-code.png">
            <img class="mx-auto" alt="img" src="https://luckly007.oss-cn-beijing.aliyuncs.com/img/1495854753-3864-converted-code.png" />
        </a>
    </p>
<h3 id="工程中配置-kotlin">工程中配置 Kotlin</h3>
<p>在开始编辑此文件时，Android Studio 会提示当前工程还未配置 Kotlin，根据提示完成操作即可，或者可以在菜单栏中选择 Tools</p>
<p>
        <a data-fancybox="gallery" href="https://luckly007.oss-cn-beijing.aliyuncs.com/img/1495854757-6620-kotlin-not-configured.png">
            <img class="mx-auto" alt="img" src="https://luckly007.oss-cn-beijing.aliyuncs.com/img/1495854757-6620-kotlin-not-configured.png" />
        </a>
    </p>
<p>选择配置时有如下对话框，选择已安装的最新版本即可。</p>
<p>
        <a data-fancybox="gallery" href="https://luckly007.oss-cn-beijing.aliyuncs.com/img/1495854752-7001-re-kotlin-in-project-details.png">
            <img class="mx-auto" alt="img" src="https://luckly007.oss-cn-beijing.aliyuncs.com/img/1495854752-7001-re-kotlin-in-project-details.png" />
        </a>
    </p>
<p>Kotlin 配置完成后，应用程序的 build.gradle 文件会更新。 你能看到新增了 apply plugin: &lsquo;kotlin-android&rsquo; 及其依赖。</p>
<p>
        <a data-fancybox="gallery" href="https://luckly007.oss-cn-beijing.aliyuncs.com/img/1495854750-1825-sync-project-with-gradle.png">
            <img class="mx-auto" alt="img" src="https://luckly007.oss-cn-beijing.aliyuncs.com/img/1495854750-1825-sync-project-with-gradle.png" />
        </a>
    </p>
<p>同步工程，在提示框中点击&quot;立即同步(Sync Now)&ldquo;或者使用 Sync Project with Gradle Files命令。</p>
<p>
        <a data-fancybox="gallery" href="https://luckly007.oss-cn-beijing.aliyuncs.com/img/1495854764-6190-sync-project-with-gradle-2.png">
            <img class="mx-auto" alt="img" src="https://luckly007.oss-cn-beijing.aliyuncs.com/img/1495854764-6190-sync-project-with-gradle-2.png" />
        </a>
    </p>
<h1 id="kotlin-基础语法">Kotlin 基础语法</h1>
<p>Kotlin 文件以 .kt 为后缀。</p>
<h2 id="包声明">包声明</h2>
<p>代码文件的开头一般为包的声明：</p>
<pre><code>com.breeze.main


import java.util.*

fun test() {}
class Breeze {}
</code></pre><p>kotlin源文件不需要相匹配的目录和包，源文件可以放在任何文件目录。</p>
<p>以上例中 test() 的全名是com.breeze.main.test、Breeze的全名是 com.breeze.main.Breeze。</p>
<p>如果没有指定包，默认为 <strong>default</strong> 包。</p>
<h3 id="默认导入">默认导入</h3>
<p>有多个包会默认导入到每个 Kotlin 文件中：</p>
<ul>
<li>kotlin.*</li>
<li>kotlin.annotation.*</li>
<li>kotlin.collections.*</li>
<li>kotlin.comparisons.*</li>
<li>kotlin.io.*</li>
<li>kotlin.ranges.*</li>
<li>kotlin.sequences.*</li>
<li>kotlin.text.*</li>
</ul>
<hr>
<h2 id="函数定义">函数定义</h2>
<p>函数定义使用关键字 fun，参数格式为：参数 : 类型</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">sum</span>(a: Int, b: Int): Int {   <span style="color:#75715e">// Int 参数，返回值 Int
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">return</span> a + b
}
</code></pre></div><p>表达式作为函数体，返回类型自动推断：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">sum</span>(a: Int, b: Int) = a + b

<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">sum</span>(a: Int, b: Int): Int = a + b   <span style="color:#75715e">// public 方法则必须明确写出返回类型
</span></code></pre></div><p>无返回值的函数(类似Java中的void)：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">printSum</span>(a: Int, b: Int): Unit { 
    print(a + b)
}


<span style="color:#75715e">// 如果是返回 Unit类型，则可以省略(对于public方法也是这样)：
</span><span style="color:#75715e"></span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">printSum</span>(a: Int, b: Int) { 
    print(a + b)
}
</code></pre></div><h3 id="可变长参数函数">可变长参数函数</h3>
<p>函数的变长参数可以用 <strong>vararg</strong> 关键字进行标识：</p>
<pre><code>fun vars(vararg v:Int){
    for(vt in v){
        print(vt)
    }
}

// 测试
fun main(args: Array&lt;String&gt;) {
    vars(1,2,3,4,5)  // 输出12345
}
</code></pre><h3 id="lambda匿名函数">lambda(匿名函数)</h3>
<p>lambda表达式使用实例：</p>
<p><a href="https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS41LjMwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiZnVuIG1haW4oYXJnczogQXJyYXk8U3RyaW5nPikge1xuICAgIHZhbCBzdW1MYW1iZGE6IChJbnQsIEludCkgLT4gSW50ID0ge3gseSAtPiB4K3l9XG4gICAgcHJpbnRsbihzdW1MYW1iZGEoMSwyKSkgIC8vIOi+k+WHuiAzXG59In0=">https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS41LjMwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiZnVuIG1haW4oYXJnczogQXJyYXk8U3RyaW5nPikge1xuICAgIHZhbCBzdW1MYW1iZGE6IChJbnQsIEludCkgLT4gSW50ID0ge3gseSAtPiB4K3l9XG4gICAgcHJpbnRsbihzdW1MYW1iZGEoMSwyKSkgIC8vIOi+k+WHuiAzXG59In0=</a></p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#75715e">// 测试
</span><span style="color:#75715e"></span><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> sumLambda: (Int, Int) <span style="color:#f92672">-&gt;</span> Int = {x,y <span style="color:#f92672">-&gt;</span> x+y}
    println(sumLambda(<span style="color:#ae81ff">1</span>,<span style="color:#ae81ff">2</span>))  <span style="color:#75715e">// 输出 3
</span><span style="color:#75715e"></span>}
</code></pre></div><hr>
<h2 id="定义常量与变量">定义常量与变量</h2>
<p>可变变量定义：var 关键字</p>
<pre><code>var &lt;标识符&gt; : &lt;类型&gt; = &lt;初始化值&gt;
</code></pre><p>不可变变量定义：val 关键字，只能赋值一次的变量(类似Java中final修饰的变量)</p>
<pre><code>val &lt;标识符&gt; : &lt;类型&gt; = &lt;初始化值&gt;
</code></pre><p>常量与变量都可以没有初始化值,但是在引用前必须初始化</p>
<p>编译器支持自动类型判断,即声明时可以不指定类型,由编译器判断。</p>
<pre><code>val a: Int = 1
val b = 1       // 系统自动推断变量类型为Int
val c: Int      // 如果不在声明时初始化则必须提供变量类型
c = 1           // 明确赋值


var x = 5        // 系统自动推断变量类型为Int
x += 1           // 变量可修改
</code></pre><hr>
<h2 id="注释">注释</h2>
<p>Kotlin 支持单行和多行注释，实例如下：</p>
<pre><code>// 这是一个单行注释

/* 这是一个多行的
   块注释。 */
</code></pre><p>与 Java 不同, Kotlin 中的块注释允许嵌套。</p>
<hr>
<h2 id="字符串模板">字符串模板</h2>
<p>$ 表示一个变量名或者变量值</p>
<p>$varName 表示变量值</p>
<p>${varName.fun()} 表示变量的方法返回值:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">var</span> a = <span style="color:#ae81ff">1</span>
<span style="color:#75715e">// 模板中的简单名称：
</span><span style="color:#75715e"></span><span style="color:#66d9ef">val</span> s1 = <span style="color:#e6db74">&#34;a is </span><span style="color:#e6db74">$a</span><span style="color:#e6db74">&#34;</span> 

a = <span style="color:#ae81ff">2</span>
<span style="color:#75715e">// 模板中的任意表达式：
</span><span style="color:#75715e"></span><span style="color:#66d9ef">val</span> s2 = <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${s1.replace(&#34;is&#34;, &#34;was&#34;)}</span><span style="color:#e6db74">, but now is </span><span style="color:#e6db74">$a</span><span style="color:#e6db74">&#34;</span>
</code></pre></div><hr>
<h2 id="null检查机制">NULL检查机制</h2>
<p>Kotlin的空安全设计对于声明可为空的参数，在使用时要进行空判断处理，有两种处理方式，字段后加!!像Java一样抛出空异常，另一种字段后加?可不做处理返回值为 null或配合?:做空判断处理</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#75715e">//类型后面加?表示可为空
</span><span style="color:#75715e"></span><span style="color:#66d9ef">var</span> age: String? = <span style="color:#e6db74">&#34;23&#34;</span> 
<span style="color:#75715e">//抛出空指针异常
</span><span style="color:#75715e"></span><span style="color:#66d9ef">val</span> ages = age<span style="color:#f92672">!!</span>.toInt()
<span style="color:#75715e">//不做处理返回 null
</span><span style="color:#75715e"></span><span style="color:#66d9ef">val</span> ages1 = age<span style="color:#f92672">?.</span>toInt()
<span style="color:#75715e">//age为空返回-1
</span><span style="color:#75715e"></span><span style="color:#66d9ef">val</span> ages2 = age<span style="color:#f92672">?.</span>toInt() <span style="color:#f92672">?:</span> -<span style="color:#ae81ff">1</span>
</code></pre></div><p>当一个引用可能为 null 值时, 对应的类型声明必须明确地标记为可为 null。</p>
<p>当 str 中的字符串内容不是一个整数时, 返回 null:</p>
<pre><code>fun parseInt(str: String): Int? {
  // ...
}
</code></pre><p>以下实例演示如何使用一个返回值可为 null 的函数:</p>
<p><a href="https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS41LjMwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiZnVuIG1haW4oYXJnczogQXJyYXk8U3RyaW5nPikge1xuICBpZiAoYXJncy5zaXplIDwgMikge1xuICAgIHByaW50KFwiVHdvIGludGVnZXJzIGV4cGVjdGVkXCIpXG4gICAgcmV0dXJuXG4gIH1cbiAgdmFsIHggPSBwYXJzZUludChhcmdzWzBdKVxuICB2YWwgeSA9IHBhcnNlSW50KGFyZ3NbMV0pXG4gIC8vIOebtOaOpeS9v+eUqCBgeCAqIHlgIOS8muWvvOiHtOmUmeivrywg5Zug5Li65a6D5Lus5Y+v6IO95Li6IG51bGwuXG4gIGlmICh4ICE9IG51bGwgJiYgeSAhPSBudWxsKSB7XG4gICAgLy8g5Zyo6L+b6KGM6L+HIG51bGwg5YC85qOA5p+l5LmL5ZCOLCB4IOWSjCB5IOeahOexu+Wei+S8muiiq+iHquWKqOi9rOaNouS4uumdniBudWxsIOWPmOmHj1xuICAgIHByaW50KHggKiB5KVxuICB9XG59In0=">https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS41LjMwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiZnVuIG1haW4oYXJnczogQXJyYXk8U3RyaW5nPikge1xuICBpZiAoYXJncy5zaXplIDwgMikge1xuICAgIHByaW50KFwiVHdvIGludGVnZXJzIGV4cGVjdGVkXCIpXG4gICAgcmV0dXJuXG4gIH1cbiAgdmFsIHggPSBwYXJzZUludChhcmdzWzBdKVxuICB2YWwgeSA9IHBhcnNlSW50KGFyZ3NbMV0pXG4gIC8vIOebtOaOpeS9v+eUqCBgeCAqIHlgIOS8muWvvOiHtOmUmeivrywg5Zug5Li65a6D5Lus5Y+v6IO95Li6IG51bGwuXG4gIGlmICh4ICE9IG51bGwgJiYgeSAhPSBudWxsKSB7XG4gICAgLy8g5Zyo6L+b6KGM6L+HIG51bGwg5YC85qOA5p+l5LmL5ZCOLCB4IOWSjCB5IOeahOexu+Wei+S8muiiq+iHquWKqOi9rOaNouS4uumdniBudWxsIOWPmOmHj1xuICAgIHByaW50KHggKiB5KVxuICB9XG59In0=</a></p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
  <span style="color:#66d9ef">if</span> (args.size &lt; <span style="color:#ae81ff">2</span>) {
    print(<span style="color:#e6db74">&#34;Two integers expected&#34;</span>)
    <span style="color:#66d9ef">return</span>
  }
  <span style="color:#66d9ef">val</span> x = parseInt(args[<span style="color:#ae81ff">0</span>])
  <span style="color:#66d9ef">val</span> y = parseInt(args[<span style="color:#ae81ff">1</span>])
  <span style="color:#75715e">// 直接使用 `x * y` 会导致错误, 因为它们可能为 null.
</span><span style="color:#75715e"></span>  <span style="color:#66d9ef">if</span> (x <span style="color:#f92672">!=</span> <span style="color:#66d9ef">null</span> <span style="color:#f92672">&amp;&amp;</span> y <span style="color:#f92672">!=</span> <span style="color:#66d9ef">null</span>) {
    <span style="color:#75715e">// 在进行过 null 值检查之后, x 和 y 的类型会被自动转换为非 null 变量
</span><span style="color:#75715e"></span>    print(x * y)
  }
}
</code></pre></div><hr>
<h2 id="类型检测及自动类型转换">类型检测及自动类型转换</h2>
<p>我们可以使用 is 运算符检测一个表达式是否某类型的一个实例(类似于Java中的instanceof关键字)。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">getStringLength</span>(obj: Any): Int? {
  <span style="color:#66d9ef">if</span> (obj <span style="color:#66d9ef">is</span> String) {
    <span style="color:#75715e">// 做过类型判断以后，obj会被系统自动转换为String类型
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">return</span> obj.length 
  }

  <span style="color:#75715e">//在这里还有一种方法，与Java中instanceof不同，使用!is
</span><span style="color:#75715e"></span>  <span style="color:#75715e">// if (obj !is String){
</span><span style="color:#75715e"></span>  <span style="color:#75715e">//   // XXX
</span><span style="color:#75715e"></span>  <span style="color:#75715e">// }
</span><span style="color:#75715e"></span>
  <span style="color:#75715e">// 这里的obj仍然是Any类型的引用
</span><span style="color:#75715e"></span>  <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">null</span>
}
</code></pre></div><p>或者</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">getStringLength</span>(obj: Any): Int? {
  <span style="color:#66d9ef">if</span> (obj <span style="color:#f92672">!is</span> String)
    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">null</span>
  <span style="color:#75715e">// 在这个分支中, `obj` 的类型会被自动转换为 `String`
</span><span style="color:#75715e"></span>  <span style="color:#66d9ef">return</span> obj.length
}
</code></pre></div><p>甚至还可以</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">getStringLength</span>(obj: Any): Int? {
  <span style="color:#75715e">// 在 `&amp;&amp;` 运算符的右侧, `obj` 的类型会被自动转换为 `String`
</span><span style="color:#75715e"></span>  <span style="color:#66d9ef">if</span> (obj <span style="color:#66d9ef">is</span> String <span style="color:#f92672">&amp;&amp;</span> obj.length &gt; <span style="color:#ae81ff">0</span>)
    <span style="color:#66d9ef">return</span> obj.length
  <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">null</span>
}
</code></pre></div><hr>
<h2 id="区间">区间</h2>
<p>区间表达式由具有操作符形式 <strong>..</strong> 的 rangeTo 函数辅以 in 和 !in 形成。</p>
<p>区间是为任何可比较类型定义的，但对于整型原生类型，它有一个优化的实现。以下是使用区间的一些示例:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">for</span> (i <span style="color:#66d9ef">in</span> <span style="color:#ae81ff">1.</span>.<span style="color:#ae81ff">4</span>) print(i) <span style="color:#75715e">// 输出“1234”
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">for</span> (i <span style="color:#66d9ef">in</span> <span style="color:#ae81ff">4.</span>.<span style="color:#ae81ff">1</span>) print(i) <span style="color:#75715e">// 什么都不输出
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">if</span> (i <span style="color:#66d9ef">in</span> <span style="color:#ae81ff">1.</span>.<span style="color:#ae81ff">10</span>) { <span style="color:#75715e">// 等同于 1 &lt;= i &amp;&amp; i &lt;= 10
</span><span style="color:#75715e"></span>    println(i)
}

<span style="color:#75715e">// 使用 step 指定步长
</span><span style="color:#75715e"></span><span style="color:#66d9ef">for</span> (i <span style="color:#66d9ef">in</span> <span style="color:#ae81ff">1.</span>.<span style="color:#ae81ff">4</span> step <span style="color:#ae81ff">2</span>) print(i) <span style="color:#75715e">// 输出“13”
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">for</span> (i <span style="color:#66d9ef">in</span> <span style="color:#ae81ff">4</span> downTo <span style="color:#ae81ff">1</span> step <span style="color:#ae81ff">2</span>) print(i) <span style="color:#75715e">// 输出“42”
</span><span style="color:#75715e"></span>

<span style="color:#75715e">// 使用 until 函数排除结束元素
</span><span style="color:#75715e"></span><span style="color:#66d9ef">for</span> (i <span style="color:#66d9ef">in</span> <span style="color:#ae81ff">1</span> until <span style="color:#ae81ff">10</span>) {   <span style="color:#75715e">// i in [1, 10) 排除了 10
</span><span style="color:#75715e"></span>     println(i)
}
</code></pre></div><h3 id="实例测试">实例测试</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    print(<span style="color:#e6db74">&#34;循环输出：&#34;</span>)
    <span style="color:#66d9ef">for</span> (i <span style="color:#66d9ef">in</span> <span style="color:#ae81ff">1.</span>.<span style="color:#ae81ff">4</span>) print(i) <span style="color:#75715e">// 输出“1234”
</span><span style="color:#75715e"></span>    println(<span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">----------------&#34;</span>)
    print(<span style="color:#e6db74">&#34;设置步长：&#34;</span>)
    <span style="color:#66d9ef">for</span> (i <span style="color:#66d9ef">in</span> <span style="color:#ae81ff">1.</span>.<span style="color:#ae81ff">4</span> step <span style="color:#ae81ff">2</span>) print(i) <span style="color:#75715e">// 输出“13”
</span><span style="color:#75715e"></span>    println(<span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">----------------&#34;</span>)
    print(<span style="color:#e6db74">&#34;使用 downTo：&#34;</span>)
    <span style="color:#66d9ef">for</span> (i <span style="color:#66d9ef">in</span> <span style="color:#ae81ff">4</span> downTo <span style="color:#ae81ff">1</span> step <span style="color:#ae81ff">2</span>) print(i) <span style="color:#75715e">// 输出“42”
</span><span style="color:#75715e"></span>    println(<span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">----------------&#34;</span>)
    print(<span style="color:#e6db74">&#34;使用 until：&#34;</span>)
    <span style="color:#75715e">// 使用 until 函数排除结束元素
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">for</span> (i <span style="color:#66d9ef">in</span> <span style="color:#ae81ff">1</span> until <span style="color:#ae81ff">4</span>) {   <span style="color:#75715e">// i in [1, 4) 排除了 4
</span><span style="color:#75715e"></span>        print(i)
    }
    println(<span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">----------------&#34;</span>)
}
</code></pre></div><p>输出结果：</p>
<pre><code>循环输出：1234
----------------
设置步长：13
----------------
使用 downTo：42
----------------
使用 until：123
</code></pre><h1 id="kotlin-基本数据类型">kotlin 基本数据类型</h1>
<p>Kotlin 的基本数值类型包括 Byte、Short、Int、Long、Float、Double 等。不同于 Java 的是，字符不属于数值类型，是一个独立的数据类型。</p>
<table>
<thead>
<tr>
<th style="text-align:left">类型</th>
<th style="text-align:left">位宽度</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">Double</td>
<td style="text-align:left">64</td>
</tr>
<tr>
<td style="text-align:left">Float</td>
<td style="text-align:left">32</td>
</tr>
<tr>
<td style="text-align:left">Long</td>
<td style="text-align:left">64</td>
</tr>
<tr>
<td style="text-align:left">Int</td>
<td style="text-align:left">32</td>
</tr>
<tr>
<td style="text-align:left">Short</td>
<td style="text-align:left">16</td>
</tr>
<tr>
<td style="text-align:left">Byte</td>
<td style="text-align:left">8</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="字面常量">字面常量</h2>
<p>下面是所有类型的字面常量：</p>
<ul>
<li>十进制：123</li>
<li>长整型以大写的 L 结尾：123L</li>
<li>16 进制以 0x 开头：0x0F</li>
<li>2 进制以 0b 开头：0b00001011</li>
<li>注意：8进制不支持</li>
</ul>
<p>Kotlin 同时也支持传统符号表示的浮点数值：</p>
<ul>
<li>Doubles 默认写法: <code>123.5</code>, <code>123.5e10</code></li>
<li>Floats 使用 f 或者 F 后缀：<code>123.5f</code></li>
</ul>
<p>你可以使用下划线使数字常量更易读：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">val</span> oneMillion = <span style="color:#ae81ff">1</span>_000_000
<span style="color:#66d9ef">val</span> creditCardNumber = <span style="color:#ae81ff">1234</span>_5678_9012_3456L
<span style="color:#66d9ef">val</span> socialSecurityNumber = <span style="color:#ae81ff">999</span>_99_9999L
<span style="color:#66d9ef">val</span> hexBytes = <span style="color:#ae81ff">0xFF</span>_EC_DE_5E
<span style="color:#66d9ef">val</span> bytes = <span style="color:#ae81ff">0</span>b11010010_01101001_10010100_10010010
</code></pre></div><hr>
<h2 id="比较两个数字">比较两个数字</h2>
<p>Kotlin 中没有基础数据类型，只有封装的数字类型，你每定义的一个变量，其实 Kotlin 帮你封装了一个对象，这样可以保证不会出现空指针。数字类型也一样，所以在比较两个数字的时候，就有比较数据大小和比较两个对象是否相同的区别了。</p>
<p>在 Kotlin 中，三个等号 === 表示比较对象地址，两个 == 表示比较两个值大小。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> a: Int = <span style="color:#ae81ff">10000</span>
    println(a <span style="color:#f92672">===</span> a) <span style="color:#75715e">// true，值相等，对象地址相等
</span><span style="color:#75715e"></span>
    <span style="color:#75715e">//经过了装箱，创建了两个不同的对象
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">val</span> boxedA: Int? = a
    <span style="color:#66d9ef">val</span> anotherBoxedA: Int? = a

    <span style="color:#75715e">//虽然经过了装箱，但是值是相等的，都是10000
</span><span style="color:#75715e"></span>    println(boxedA <span style="color:#f92672">===</span> anotherBoxedA) <span style="color:#75715e">//  false，值相等，对象地址不一样
</span><span style="color:#75715e"></span>    println(boxedA <span style="color:#f92672">==</span> anotherBoxedA) <span style="color:#75715e">// true，值相等
</span><span style="color:#75715e"></span>}
</code></pre></div><h2 id="类型转换">类型转换</h2>
<p>由于不同的表示方式，较小类型并不是较大类型的子类型，较小的类型不能隐式转换为较大的类型。 这意味着在不进行显式转换的情况下我们不能把 Byte 型值赋给一个 Int 变量。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">val</span> b: Byte = <span style="color:#ae81ff">1</span> <span style="color:#75715e">// OK, 字面值是静态检测的
</span><span style="color:#75715e"></span><span style="color:#66d9ef">val</span> i: Int = b <span style="color:#75715e">// 错误
</span></code></pre></div><p>我们可以代用其toInt()方法。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">val</span> b: Byte = <span style="color:#ae81ff">1</span> <span style="color:#75715e">// OK, 字面值是静态检测的
</span><span style="color:#75715e"></span><span style="color:#66d9ef">val</span> i: Int = b.toInt() <span style="color:#75715e">// OK
</span></code></pre></div><p>每种数据类型都有下面的这些方法，可以转化为其它的类型：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char
</code></pre></div><p>有些情况下也是可以使用自动类型转化的，前提是可以根据上下文环境推断出正确的数据类型而且数学操作符会做相应的重载。例如下面是正确的：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">val</span> l = <span style="color:#ae81ff">1L</span> + <span style="color:#ae81ff">3</span> <span style="color:#75715e">// Long + Int =&gt; Long
</span></code></pre></div><hr>
<h2 id="位操作符">位操作符</h2>
<p>对于Int和Long类型，还有一系列的位操作符可以使用，分别是：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">shl(bits) <span style="color:#960050;background-color:#1e0010">–</span> <span style="color:#960050;background-color:#1e0010">左移位</span> (Java<span style="color:#960050;background-color:#1e0010">’</span>s &lt;&lt;)
shr(bits) <span style="color:#960050;background-color:#1e0010">–</span> <span style="color:#960050;background-color:#1e0010">右移位</span> (Java<span style="color:#960050;background-color:#1e0010">’</span>s &gt;&gt;)
ushr(bits) <span style="color:#960050;background-color:#1e0010">–</span> <span style="color:#960050;background-color:#1e0010">无符号右移位</span> (Java<span style="color:#960050;background-color:#1e0010">’</span>s &gt;&gt;&gt;)
and(bits) <span style="color:#960050;background-color:#1e0010">–</span> <span style="color:#960050;background-color:#1e0010">与</span>
or(bits) <span style="color:#960050;background-color:#1e0010">–</span> <span style="color:#960050;background-color:#1e0010">或</span>
xor(bits) <span style="color:#960050;background-color:#1e0010">–</span> <span style="color:#960050;background-color:#1e0010">异或</span>
inv() <span style="color:#960050;background-color:#1e0010">–</span> <span style="color:#960050;background-color:#1e0010">反向</span>
</code></pre></div><hr>
<h2 id="字符">字符</h2>
<p>和 Java 不一样，Kotlin 中的 Char 不能直接和数字操作，Char 必需是单引号 <strong>'</strong> 包含起来的。比如普通字符 &lsquo;0&rsquo;，&lsquo;a&rsquo;。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">check</span>(c: Char) {
    <span style="color:#66d9ef">if</span> (c <span style="color:#f92672">==</span> <span style="color:#ae81ff">1</span>) { <span style="color:#75715e">// 错误：类型不兼容
</span><span style="color:#75715e"></span>        <span style="color:#75715e">// ……
</span><span style="color:#75715e"></span>    }
}
</code></pre></div><p>字符字面值用单引号括起来: &lsquo;1&rsquo;。 特殊字符可以用反斜杠转义。 支持这几个转义序列：\t、 \b、\n、\r、'、&quot;、\ 和 $。 编码其他字符要用 Unicode 转义序列语法：'\uFF00'。</p>
<p>我们可以显式把字符转换为 Int 数字：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">decimalDigitValue</span>(c: Char): Int {
    <span style="color:#66d9ef">if</span> (c <span style="color:#f92672">!in</span> <span style="color:#e6db74">&#39;0&#39;</span><span style="color:#f92672">..</span><span style="color:#e6db74">&#39;9&#39;</span>)
        <span style="color:#66d9ef">throw</span> IllegalArgumentException(<span style="color:#e6db74">&#34;Out of range&#34;</span>)
    <span style="color:#66d9ef">return</span> c.toInt() - <span style="color:#e6db74">&#39;0&#39;</span>.toInt() <span style="color:#75715e">// 显式转换为数字
</span><span style="color:#75715e"></span>}
</code></pre></div><p>当需要可空引用时，像数字、字符会被装箱。装箱操作不会保留同一性。</p>
<hr>
<h2 id="布尔">布尔</h2>
<p>布尔用 Boolean 类型表示，它有两个值：true 和 false。</p>
<p>若需要可空引用布尔会被装箱。</p>
<p>内置的布尔运算有：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#f92672">||</span> <span style="color:#960050;background-color:#1e0010">–</span> <span style="color:#960050;background-color:#1e0010">短路逻辑或</span>
<span style="color:#f92672">&amp;&amp;</span> <span style="color:#960050;background-color:#1e0010">–</span> <span style="color:#960050;background-color:#1e0010">短路逻辑与</span>
! - <span style="color:#960050;background-color:#1e0010">逻辑非</span>
</code></pre></div><hr>
<h2 id="数组">数组</h2>
<p>数组用类 Array 实现，并且还有一个 size 属性及 get 和 set 方法，由于使用 [] 重载了 get 和 set 方法，所以我们可以通过下标很方便的获取或者设置数组对应位置的值。</p>
<p>数组的创建两种方式：一种是使用函数arrayOf()；另外一种是使用工厂函数。如下所示，我们分别是两种方式创建了两个数组：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#75715e">//[1,2,3]
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">val</span> a = arrayOf(<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">2</span>, <span style="color:#ae81ff">3</span>)
    <span style="color:#75715e">//[0,2,4]
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">val</span> b = Array(<span style="color:#ae81ff">3</span>, { i <span style="color:#f92672">-&gt;</span> (i * <span style="color:#ae81ff">2</span>) })

    <span style="color:#75715e">//读取数组内容
</span><span style="color:#75715e"></span>    println(a[<span style="color:#ae81ff">0</span>])    <span style="color:#75715e">// 输出结果：1
</span><span style="color:#75715e"></span>    println(b[<span style="color:#ae81ff">1</span>])    <span style="color:#75715e">// 输出结果：2
</span><span style="color:#75715e"></span>}
</code></pre></div><p>如上所述，[] 运算符代表调用成员函数 get() 和 set()。</p>
<p>注意: 与 Java 不同的是，Kotlin 中数组是不协变的(invariant)。</p>
<p>除了类Array，还有ByteArray, ShortArray, IntArray，用来表示各个类型的数组，省去了装箱操作，因此效率更高，其用法同Array一样：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">val</span> x: IntArray = intArrayOf(<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">2</span>, <span style="color:#ae81ff">3</span>)
x[<span style="color:#ae81ff">0</span>] = x[<span style="color:#ae81ff">1</span>] + x[<span style="color:#ae81ff">2</span>]
</code></pre></div><hr>
<h2 id="字符串">字符串</h2>
<p>和 Java 一样，String 是不可变的。方括号 [] 语法可以很方便的获取字符串中的某个字符，也可以通过 for 循环来遍历：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">for</span> (c <span style="color:#66d9ef">in</span> str) {
    println(c)
}
</code></pre></div><p>Kotlin 支持三个引号 &quot;&rdquo;&rdquo; 扩起来的字符串，支持多行字符串，比如：</p>
<p><a href="https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS41LjMwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiZnVuIG1haW4oYXJnczogQXJyYXk8U3RyaW5nPikge1xuICAgIHZhbCB0ZXh0ID0gXCJcIlwiXG4gICAg5aSa6KGM5a2X56ym5LiyXG4gICAg5aSa6KGM5a2X56ym5LiyXG4gICAgXCJcIlwiXG4gICAgcHJpbnRsbih0ZXh0KSAgIC8vIOi+k+WHuuacieS4gOS6m+WJjee9ruepuuagvFxufSJ9">https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS41LjMwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiZnVuIG1haW4oYXJnczogQXJyYXk8U3RyaW5nPikge1xuICAgIHZhbCB0ZXh0ID0gXCJcIlwiXG4gICAg5aSa6KGM5a2X56ym5LiyXG4gICAg5aSa6KGM5a2X56ym5LiyXG4gICAgXCJcIlwiXG4gICAgcHJpbnRsbih0ZXh0KSAgIC8vIOi+k+WHuuacieS4gOS6m+WJjee9ruepuuagvFxufSJ9</a></p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> text = <span style="color:#e6db74">&#34;&#34;&#34;
</span><span style="color:#e6db74">    多行字符串
</span><span style="color:#e6db74">    多行字符串
</span><span style="color:#e6db74">    &#34;&#34;&#34;</span>
    println(text)   <span style="color:#75715e">// 输出有一些前置空格
</span><span style="color:#75715e"></span>}
</code></pre></div><p>String 可以通过 trimMargin() 方法来删除多余的空白。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> text = <span style="color:#e6db74">&#34;&#34;&#34;
</span><span style="color:#e6db74">    |多行字符串
</span><span style="color:#e6db74">    |菜鸟教程
</span><span style="color:#e6db74">    |多行字符串
</span><span style="color:#e6db74">    |Breeze
</span><span style="color:#e6db74">    &#34;&#34;&#34;</span>.trimMargin()
    println(text)    <span style="color:#75715e">// 前置空格删除了
</span><span style="color:#75715e"></span>}
</code></pre></div><p>默认 | 用作边界前缀，但你可以选择其他字符并作为参数传入，比如 trimMargin(&quot;&gt;&quot;)。</p>
<hr>
<h2 id="字符串模板-1">字符串模板</h2>
<p>字符串可以包含模板表达式 ，即一些小段代码，会求值并把结果合并到字符串中。 模板表达式以美元符($)开头，由一个简单的名字构成:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> i = <span style="color:#ae81ff">10</span>
    <span style="color:#66d9ef">val</span> s = <span style="color:#e6db74">&#34;i = </span><span style="color:#e6db74">$i</span><span style="color:#e6db74">&#34;</span> <span style="color:#75715e">// 求值结果为 &#34;i = 10&#34;
</span><span style="color:#75715e"></span>    println(s)
}
</code></pre></div><p>或者用花括号扩起来的任意表达式:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> s = <span style="color:#e6db74">&#34;breeze&#34;</span>
    <span style="color:#66d9ef">val</span> str = <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">$s</span><span style="color:#e6db74">.length is </span><span style="color:#e6db74">${s.length}</span><span style="color:#e6db74">&#34;</span> <span style="color:#75715e">// 求值结果为 &#34;breeze.length is 6&#34;
</span><span style="color:#75715e"></span>    println(str)
}
</code></pre></div><p>原生字符串和转义字符串内部都支持模板。 如果你需要在原生字符串中表示字面值 $ 字符(它不支持反斜杠转义)，你可以用下列语法：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> price = <span style="color:#e6db74">&#34;&#34;&#34;
</span><span style="color:#e6db74">    </span><span style="color:#e6db74">${&#39;$&#39;}</span><span style="color:#e6db74">9.99
</span><span style="color:#e6db74">    &#34;&#34;&#34;</span>
    println(price)  <span style="color:#75715e">// 求值结果为 $9.99
</span><span style="color:#75715e"></span>}
</code></pre></div><h1 id="kotlin-条件控制">Kotlin 条件控制</h1>
<h2 id="if-表达式">IF 表达式</h2>
<p>一个 if 语句包含一个布尔表达式和一条或多条语句。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#75715e">// 传统用法
</span><span style="color:#75715e"></span><span style="color:#66d9ef">var</span> max = a 
<span style="color:#66d9ef">if</span> (a &lt; b) max = b

<span style="color:#75715e">// 使用 else 
</span><span style="color:#75715e"></span><span style="color:#66d9ef">var</span> max: Int
<span style="color:#66d9ef">if</span> (a &gt; b) {
    max = a
} <span style="color:#66d9ef">else</span> {
    max = b
}
 
<span style="color:#75715e">// 作为表达式
</span><span style="color:#75715e"></span><span style="color:#66d9ef">val</span> max = <span style="color:#66d9ef">if</span> (a &gt; b) a <span style="color:#66d9ef">else</span> b
</code></pre></div><p>我们也可以把 IF 表达式的结果赋值给一个变量。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">val</span> max = <span style="color:#66d9ef">if</span> (a &gt; b) {
    print(<span style="color:#e6db74">&#34;Choose a&#34;</span>)
    a
} <span style="color:#66d9ef">else</span> {
    print(<span style="color:#e6db74">&#34;Choose b&#34;</span>)
    b
}
</code></pre></div><p>这也说明我也不需要像Java那种有一个三元操作符，因为我们可以使用它来简单实现：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">val</span> c = <span style="color:#66d9ef">if</span> (condition) a <span style="color:#66d9ef">else</span> b
</code></pre></div><h3 id="实例">实例</h3>
<p><a href="https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS41LjMwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiZnVuIG1haW4oYXJnczogQXJyYXk8U3RyaW5nPikge1xuICAgIHZhciB4ID0gMFxuICAgIGlmKHg+MCl7XG4gICAgICAgIHByaW50bG4oXCJ4IOWkp+S6jiAwXCIpXG4gICAgfWVsc2UgaWYoeD09MCl7XG4gICAgICAgIHByaW50bG4oXCJ4IOetieS6jiAwXCIpXG4gICAgfWVsc2V7XG4gICAgICAgIHByaW50bG4oXCJ4IOWwj+S6jiAwXCIpXG4gICAgfVxuXG4gICAgdmFyIGEgPSAxXG4gICAgdmFyIGIgPSAyXG4gICAgdmFsIGMgPSBpZiAoYT49YikgYSBlbHNlIGJcbiAgICBwcmludGxuKFwiYyDnmoTlgLzkuLogJGNcIilcbn0ifQ==">https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS41LjMwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiZnVuIG1haW4oYXJnczogQXJyYXk8U3RyaW5nPikge1xuICAgIHZhciB4ID0gMFxuICAgIGlmKHg+MCl7XG4gICAgICAgIHByaW50bG4oXCJ4IOWkp+S6jiAwXCIpXG4gICAgfWVsc2UgaWYoeD09MCl7XG4gICAgICAgIHByaW50bG4oXCJ4IOetieS6jiAwXCIpXG4gICAgfWVsc2V7XG4gICAgICAgIHByaW50bG4oXCJ4IOWwj+S6jiAwXCIpXG4gICAgfVxuXG4gICAgdmFyIGEgPSAxXG4gICAgdmFyIGIgPSAyXG4gICAgdmFsIGMgPSBpZiAoYT49YikgYSBlbHNlIGJcbiAgICBwcmludGxuKFwiYyDnmoTlgLzkuLogJGNcIilcbn0ifQ==</a></p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">var</span> x = <span style="color:#ae81ff">0</span>
    <span style="color:#66d9ef">if</span>(x&gt;<span style="color:#ae81ff">0</span>){
        println(<span style="color:#e6db74">&#34;x 大于 0&#34;</span>)
    }<span style="color:#66d9ef">else</span> <span style="color:#66d9ef">if</span>(x<span style="color:#f92672">==</span><span style="color:#ae81ff">0</span>){
        println(<span style="color:#e6db74">&#34;x 等于 0&#34;</span>)
    }<span style="color:#66d9ef">else</span>{
        println(<span style="color:#e6db74">&#34;x 小于 0&#34;</span>)
    }

    <span style="color:#66d9ef">var</span> a = <span style="color:#ae81ff">1</span>
    <span style="color:#66d9ef">var</span> b = <span style="color:#ae81ff">2</span>
    <span style="color:#66d9ef">val</span> c = <span style="color:#66d9ef">if</span> (a<span style="color:#f92672">&gt;=</span>b) a <span style="color:#66d9ef">else</span> b
    println(<span style="color:#e6db74">&#34;c 的值为 </span><span style="color:#e6db74">$c</span><span style="color:#e6db74">&#34;</span>)
}
</code></pre></div><p>输出结果为：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">x <span style="color:#960050;background-color:#1e0010">等于</span> <span style="color:#ae81ff">0</span>
c <span style="color:#960050;background-color:#1e0010">的值为</span> <span style="color:#ae81ff">2</span>
</code></pre></div><h3 id="使用区间">使用区间</h3>
<p>使用 in 运算符来检测某个数字是否在指定区间内，区间格式为 <strong>x..y</strong> ：</p>
<p>实例</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> x = <span style="color:#ae81ff">5</span>
    <span style="color:#66d9ef">val</span> y = <span style="color:#ae81ff">9</span>
    <span style="color:#66d9ef">if</span> (x <span style="color:#66d9ef">in</span> <span style="color:#ae81ff">1.</span>.<span style="color:#ae81ff">8</span>) {
        println(<span style="color:#e6db74">&#34;x 在区间内&#34;</span>)
    }
}
</code></pre></div><p>输出结果为：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">x <span style="color:#960050;background-color:#1e0010">在区间内</span>
</code></pre></div><hr>
<h2 id="when-表达式">When 表达式</h2>
<p>when 将它的参数和所有的分支条件顺序比较，直到某个分支满足条件。</p>
<p>when 既可以被当做表达式使用也可以被当做语句使用。如果它被当做表达式，符合条件的分支的值就是整个表达式的值，如果当做语句使用， 则忽略个别分支的值。</p>
<p>when 类似其他语言的 switch 操作符。其最简单的形式如下：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">when</span> (x) {
    <span style="color:#ae81ff">1</span> <span style="color:#f92672">-&gt;</span> print(<span style="color:#e6db74">&#34;x == 1&#34;</span>)
    <span style="color:#ae81ff">2</span> <span style="color:#f92672">-&gt;</span> print(<span style="color:#e6db74">&#34;x == 2&#34;</span>)
    <span style="color:#66d9ef">else</span> <span style="color:#f92672">-&gt;</span> { <span style="color:#75715e">// 注意这个块
</span><span style="color:#75715e"></span>        print(<span style="color:#e6db74">&#34;x 不是 1 ，也不是 2&#34;</span>)
    }
}
</code></pre></div><p>在 when 中，else 同 switch 的 default。如果其他分支都不满足条件将会求值 else 分支。</p>
<p>如果很多分支需要用相同的方式处理，则可以把多个分支条件放在一起，用逗号分隔：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">when</span> (x) {
    <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span> <span style="color:#f92672">-&gt;</span> print(<span style="color:#e6db74">&#34;x == 0 or x == 1&#34;</span>)
    <span style="color:#66d9ef">else</span> <span style="color:#f92672">-&gt;</span> print(<span style="color:#e6db74">&#34;otherwise&#34;</span>)
}
</code></pre></div><p>我们也可以检测一个值在(in)或者不在(!in)一个区间或者集合中：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">when</span> (x) {
    <span style="color:#66d9ef">in</span> <span style="color:#ae81ff">1.</span>.<span style="color:#ae81ff">10</span> <span style="color:#f92672">-&gt;</span> print(<span style="color:#e6db74">&#34;x is in the range&#34;</span>)
    <span style="color:#66d9ef">in</span> validNumbers <span style="color:#f92672">-&gt;</span> print(<span style="color:#e6db74">&#34;x is valid&#34;</span>)
    <span style="color:#f92672">!in</span> <span style="color:#ae81ff">10.</span>.<span style="color:#ae81ff">20</span> <span style="color:#f92672">-&gt;</span> print(<span style="color:#e6db74">&#34;x is outside the range&#34;</span>)
    <span style="color:#66d9ef">else</span> <span style="color:#f92672">-&gt;</span> print(<span style="color:#e6db74">&#34;none of the above&#34;</span>)
}
</code></pre></div><p>另一种可能性是检测一个值是(is)或者不是(!is)一个特定类型的值。注意： 由于智能转换，你可以访问该类型的方法和属性而无需 任何额外的检测。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">hasPrefix</span>(x: Any) = <span style="color:#66d9ef">when</span>(x) {
    <span style="color:#66d9ef">is</span> String <span style="color:#f92672">-&gt;</span> x.startsWith(<span style="color:#e6db74">&#34;prefix&#34;</span>)
    <span style="color:#66d9ef">else</span> <span style="color:#f92672">-&gt;</span> <span style="color:#66d9ef">false</span>
}
</code></pre></div><p>when 也可以用来取代 if-else if链。 如果不提供参数，所有的分支条件都是简单的布尔表达式，而当一个分支的条件为真时则执行该分支：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">when</span> {
    x.isOdd() <span style="color:#f92672">-&gt;</span> print(<span style="color:#e6db74">&#34;x is odd&#34;</span>)
    x.isEven() <span style="color:#f92672">-&gt;</span> print(<span style="color:#e6db74">&#34;x is even&#34;</span>)
    <span style="color:#66d9ef">else</span> <span style="color:#f92672">-&gt;</span> print(<span style="color:#e6db74">&#34;x is funny&#34;</span>)
}
</code></pre></div><h3 id="实例-1">实例</h3>
<p><a href="https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS41LjMwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiZnVuIG1haW4oYXJnczogQXJyYXk8U3RyaW5nPikge1xuICAgIHZhciB4ID0gMFxuICAgIHdoZW4gKHgpIHtcbiAgICAgICAgMCwgMSAtPiBwcmludGxuKFwieCA9PSAwIG9yIHggPT0gMVwiKVxuICAgICAgICBlbHNlIC0+IHByaW50bG4oXCJvdGhlcndpc2VcIilcbiAgICB9XG5cbiAgICB3aGVuICh4KSB7XG4gICAgICAgIDEgLT4gcHJpbnRsbihcInggPT0gMVwiKVxuICAgICAgICAyIC0+IHByaW50bG4oXCJ4ID09IDJcIilcbiAgICAgICAgZWxzZSAtPiB7IC8vIOazqOaEj+i/meS4quWdl1xuICAgICAgICAgICAgcHJpbnRsbihcIngg5LiN5pivIDEg77yM5Lmf5LiN5pivIDJcIilcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHdoZW4gKHgpIHtcbiAgICAgICAgaW4gMC4uMTAgLT4gcHJpbnRsbihcIngg5Zyo6K+l5Yy66Ze06IyD5Zu05YaFXCIpXG4gICAgICAgIGVsc2UgLT4gcHJpbnRsbihcIngg5LiN5Zyo6K+l5Yy66Ze06IyD5Zu05YaFXCIpXG4gICAgfVxufSJ9">https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS41LjMwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiZnVuIG1haW4oYXJnczogQXJyYXk8U3RyaW5nPikge1xuICAgIHZhciB4ID0gMFxuICAgIHdoZW4gKHgpIHtcbiAgICAgICAgMCwgMSAtPiBwcmludGxuKFwieCA9PSAwIG9yIHggPT0gMVwiKVxuICAgICAgICBlbHNlIC0+IHByaW50bG4oXCJvdGhlcndpc2VcIilcbiAgICB9XG5cbiAgICB3aGVuICh4KSB7XG4gICAgICAgIDEgLT4gcHJpbnRsbihcInggPT0gMVwiKVxuICAgICAgICAyIC0+IHByaW50bG4oXCJ4ID09IDJcIilcbiAgICAgICAgZWxzZSAtPiB7IC8vIOazqOaEj+i/meS4quWdl1xuICAgICAgICAgICAgcHJpbnRsbihcIngg5LiN5pivIDEg77yM5Lmf5LiN5pivIDJcIilcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHdoZW4gKHgpIHtcbiAgICAgICAgaW4gMC4uMTAgLT4gcHJpbnRsbihcIngg5Zyo6K+l5Yy66Ze06IyD5Zu05YaFXCIpXG4gICAgICAgIGVsc2UgLT4gcHJpbnRsbihcIngg5LiN5Zyo6K+l5Yy66Ze06IyD5Zu05YaFXCIpXG4gICAgfVxufSJ9</a></p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">var</span> x = <span style="color:#ae81ff">0</span>
    <span style="color:#66d9ef">when</span> (x) {
        <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span> <span style="color:#f92672">-&gt;</span> println(<span style="color:#e6db74">&#34;x == 0 or x == 1&#34;</span>)
        <span style="color:#66d9ef">else</span> <span style="color:#f92672">-&gt;</span> println(<span style="color:#e6db74">&#34;otherwise&#34;</span>)
    }

    <span style="color:#66d9ef">when</span> (x) {
        <span style="color:#ae81ff">1</span> <span style="color:#f92672">-&gt;</span> println(<span style="color:#e6db74">&#34;x == 1&#34;</span>)
        <span style="color:#ae81ff">2</span> <span style="color:#f92672">-&gt;</span> println(<span style="color:#e6db74">&#34;x == 2&#34;</span>)
        <span style="color:#66d9ef">else</span> <span style="color:#f92672">-&gt;</span> { <span style="color:#75715e">// 注意这个块
</span><span style="color:#75715e"></span>            println(<span style="color:#e6db74">&#34;x 不是 1 ，也不是 2&#34;</span>)
        }
    }

    <span style="color:#66d9ef">when</span> (x) {
        <span style="color:#66d9ef">in</span> <span style="color:#ae81ff">0.</span>.<span style="color:#ae81ff">10</span> <span style="color:#f92672">-&gt;</span> println(<span style="color:#e6db74">&#34;x 在该区间范围内&#34;</span>)
        <span style="color:#66d9ef">else</span> <span style="color:#f92672">-&gt;</span> println(<span style="color:#e6db74">&#34;x 不在该区间范围内&#34;</span>)
    }
}
</code></pre></div><p>输出结果：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">x <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span> or x <span style="color:#f92672">==</span> <span style="color:#ae81ff">1</span>
x <span style="color:#960050;background-color:#1e0010">不是</span> <span style="color:#ae81ff">1</span> <span style="color:#960050;background-color:#1e0010">，也不是</span> <span style="color:#ae81ff">2</span>
x <span style="color:#960050;background-color:#1e0010">在该区间范围内</span>
</code></pre></div><p>when 中使用 <strong>in</strong> 运算符来判断集合内是否包含某实例：</p>
<p><a href="https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS41LjMwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiZnVuIG1haW4oYXJnczogQXJyYXk8U3RyaW5nPikge1xuICAgIHZhbCBpdGVtcyA9IHNldE9mKFwiYXBwbGVcIiwgXCJiYW5hbmFcIiwgXCJraXdpXCIpXG4gICAgd2hlbiB7XG4gICAgICAgIFwib3JhbmdlXCIgaW4gaXRlbXMgLT4gcHJpbnRsbihcImp1aWN5XCIpXG4gICAgICAgIFwiYXBwbGVcIiBpbiBpdGVtcyAtPiBwcmludGxuKFwiYXBwbGUgaXMgZmluZSB0b29cIilcbiAgICB9XG59In0=">https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS41LjMwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiZnVuIG1haW4oYXJnczogQXJyYXk8U3RyaW5nPikge1xuICAgIHZhbCBpdGVtcyA9IHNldE9mKFwiYXBwbGVcIiwgXCJiYW5hbmFcIiwgXCJraXdpXCIpXG4gICAgd2hlbiB7XG4gICAgICAgIFwib3JhbmdlXCIgaW4gaXRlbXMgLT4gcHJpbnRsbihcImp1aWN5XCIpXG4gICAgICAgIFwiYXBwbGVcIiBpbiBpdGVtcyAtPiBwcmludGxuKFwiYXBwbGUgaXMgZmluZSB0b29cIilcbiAgICB9XG59In0=</a></p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> items = setOf(<span style="color:#e6db74">&#34;apple&#34;</span>, <span style="color:#e6db74">&#34;banana&#34;</span>, <span style="color:#e6db74">&#34;kiwi&#34;</span>)
    <span style="color:#66d9ef">when</span> {
        <span style="color:#e6db74">&#34;orange&#34;</span> <span style="color:#66d9ef">in</span> items <span style="color:#f92672">-&gt;</span> println(<span style="color:#e6db74">&#34;juicy&#34;</span>)
        <span style="color:#e6db74">&#34;apple&#34;</span> <span style="color:#66d9ef">in</span> items <span style="color:#f92672">-&gt;</span> println(<span style="color:#e6db74">&#34;apple is fine too&#34;</span>)
    }
}
</code></pre></div><p>输出结果：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">apple <span style="color:#66d9ef">is</span> fine too
</code></pre></div><h1 id="kotlin-循环控制">Kotlin 循环控制</h1>
<h2 id="for-循环">For 循环</h2>
<p>for 循环可以对任何提供迭代器(iterator)的对象进行遍历，语法如下:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">for</span> (item <span style="color:#66d9ef">in</span> collection) print(item)
</code></pre></div><p>循环体可以是一个代码块:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">for</span> (item: Int <span style="color:#66d9ef">in</span> ints) {
    <span style="color:#75715e">// ……
</span><span style="color:#75715e"></span>}
</code></pre></div><p>如上所述，for 可以循环遍历任何提供了迭代器的对象。</p>
<p>如果你想要通过索引遍历一个数组或者一个 list，你可以这么做：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">for</span> (i <span style="color:#66d9ef">in</span> array.indices) {
    print(array[i])
}
</code></pre></div><p>注意这种&quot;在区间上遍历&quot;会编译成优化的实现而不会创建额外对象。</p>
<p>或者你可以用库函数 withIndex：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">for</span> ((index, value) <span style="color:#66d9ef">in</span> array.withIndex()) {
    println(<span style="color:#e6db74">&#34;the element at </span><span style="color:#e6db74">$index</span><span style="color:#e6db74"> is </span><span style="color:#e6db74">$value</span><span style="color:#e6db74">&#34;</span>)
}
</code></pre></div><h3 id="实例-2">实例</h3>
<p>对集合进行迭代：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> items = listOf(<span style="color:#e6db74">&#34;apple&#34;</span>, <span style="color:#e6db74">&#34;banana&#34;</span>, <span style="color:#e6db74">&#34;kiwi&#34;</span>)
    <span style="color:#66d9ef">for</span> (item <span style="color:#66d9ef">in</span> items) {
        println(item)
    }

    <span style="color:#66d9ef">for</span> (index <span style="color:#66d9ef">in</span> items.indices) {
        println(<span style="color:#e6db74">&#34;item at </span><span style="color:#e6db74">$index</span><span style="color:#e6db74"> is </span><span style="color:#e6db74">${items[index]}</span><span style="color:#e6db74">&#34;</span>)
    }
}
</code></pre></div><p>输出结果：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">apple
banana
kiwi
item at <span style="color:#ae81ff">0</span> <span style="color:#66d9ef">is</span> apple
item at <span style="color:#ae81ff">1</span> <span style="color:#66d9ef">is</span> banana
item at <span style="color:#ae81ff">2</span> <span style="color:#66d9ef">is</span> kiwi
</code></pre></div><hr>
<h2 id="while-与-dowhile-循环">while 与 do&hellip;while 循环</h2>
<p>while是最基本的循环，它的结构为：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">while</span>( <span style="color:#960050;background-color:#1e0010">布尔表达式</span> ) {
  <span style="color:#75715e">//循环内容
</span><span style="color:#75715e"></span>}
</code></pre></div><p>do…while 循环 对于 while 语句而言，如果不满足条件，则不能进入循环。但有时候我们需要即使不满足条件，也至少执行一次。</p>
<p>do…while 循环和 while 循环相似，不同的是，do…while 循环至少会执行一次。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">do</span> {
       <span style="color:#75715e">//代码语句
</span><span style="color:#75715e"></span>}<span style="color:#66d9ef">while</span>(<span style="color:#960050;background-color:#1e0010">布尔表达式</span>);
</code></pre></div><h3 id="实例-3">实例</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    println(<span style="color:#e6db74">&#34;----while 使用-----&#34;</span>)
    <span style="color:#66d9ef">var</span> x = <span style="color:#ae81ff">5</span>
    <span style="color:#66d9ef">while</span> (x &gt; <span style="color:#ae81ff">0</span>) {
        println( x<span style="color:#f92672">--</span>)
    }
    println(<span style="color:#e6db74">&#34;----do...while 使用-----&#34;</span>)
    <span style="color:#66d9ef">var</span> y = <span style="color:#ae81ff">5</span>
    <span style="color:#66d9ef">do</span> {
        println(y<span style="color:#f92672">--</span>)
    } <span style="color:#66d9ef">while</span>(y&gt;<span style="color:#ae81ff">0</span>)
}
</code></pre></div><p>输出结果：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#ae81ff">5</span>
<span style="color:#ae81ff">4</span>
<span style="color:#ae81ff">3</span>
<span style="color:#ae81ff">2</span>
<span style="color:#ae81ff">1</span>
<span style="color:#f92672">----</span><span style="color:#66d9ef">do</span><span style="color:#f92672">..</span>.<span style="color:#66d9ef">while</span> <span style="color:#960050;background-color:#1e0010">使用</span><span style="color:#f92672">----</span>-
<span style="color:#ae81ff">5</span>
<span style="color:#ae81ff">4</span>
<span style="color:#ae81ff">3</span>
<span style="color:#ae81ff">2</span>
<span style="color:#ae81ff">1</span>
</code></pre></div><hr>
<h2 id="返回和跳转">返回和跳转</h2>
<p>Kotlin 有三种结构化跳转表达式：</p>
<ul>
<li><em>return</em>。默认从最直接包围它的函数或者匿名函数返回。</li>
<li><em>break</em>。终止最直接包围它的循环。</li>
<li><em>continue</em>。继续下一次最直接包围它的循环。</li>
</ul>
<p>在循环中 Kotlin 支持传统的 break 和 continue 操作符。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">for</span> (i <span style="color:#66d9ef">in</span> <span style="color:#ae81ff">1.</span>.<span style="color:#ae81ff">10</span>) {
        <span style="color:#66d9ef">if</span> (i<span style="color:#f92672">==</span><span style="color:#ae81ff">3</span>) <span style="color:#66d9ef">continue</span>  <span style="color:#75715e">// i 为 3 时跳过当前循环，继续下一次循环
</span><span style="color:#75715e"></span>        println(i)
        <span style="color:#66d9ef">if</span> (i&gt;<span style="color:#ae81ff">5</span>) <span style="color:#66d9ef">break</span>   <span style="color:#75715e">// i 为 6 时 跳出循环
</span><span style="color:#75715e"></span>    }
}
</code></pre></div><p>输出结果：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#ae81ff">1</span>
<span style="color:#ae81ff">2</span>
<span style="color:#ae81ff">4</span>
<span style="color:#ae81ff">5</span>
<span style="color:#ae81ff">6</span>
</code></pre></div><h3 id="break-和-continue-标签">Break 和 Continue 标签</h3>
<p>在 Kotlin 中任何表达式都可以用标签(label)来标记。 标签的格式为标识符后跟 @ 符号，例如：abc@、fooBar@都是有效的标签。 要为一个表达式加标签，我们只要在其前加标签即可。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">loop<span style="color:#960050;background-color:#1e0010">@</span> <span style="color:#66d9ef">for</span> (i <span style="color:#66d9ef">in</span> <span style="color:#ae81ff">1.</span>.<span style="color:#ae81ff">100</span>) {
    <span style="color:#75715e">// ……
</span><span style="color:#75715e"></span>}
</code></pre></div><p>现在，我们可以用标签限制 break 或者continue：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">loop<span style="color:#960050;background-color:#1e0010">@</span> <span style="color:#66d9ef">for</span> (i <span style="color:#66d9ef">in</span> <span style="color:#ae81ff">1.</span>.<span style="color:#ae81ff">100</span>) {
    <span style="color:#66d9ef">for</span> (j <span style="color:#66d9ef">in</span> <span style="color:#ae81ff">1.</span>.<span style="color:#ae81ff">100</span>) {
        <span style="color:#66d9ef">if</span> (<span style="color:#960050;background-color:#1e0010">……</span>) <span style="color:#66d9ef">break</span><span style="color:#a6e22e">@loop</span>
    }
}
</code></pre></div><p>标签限制的 break 跳转到刚好位于该标签指定的循环后面的执行点。 continue 继续标签指定的循环的下一次迭代。</p>
<h3 id="标签处返回">标签处返回</h3>
<p>Kotlin 有函数字面量、局部函数和对象表达式。因此 Kotlin 的函数可以被嵌套。 标签限制的 return 允许我们从外层函数返回。 最重要的一个用途就是从 lambda 表达式中返回。回想一下我们这么写的时候：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">foo</span>() {
    ints.forEach {
        <span style="color:#66d9ef">if</span> (<span style="color:#66d9ef">it</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>) <span style="color:#66d9ef">return</span>
        print(<span style="color:#66d9ef">it</span>)
    }
}
</code></pre></div><p>这个 return 表达式从最直接包围它的函数即 foo 中返回。 (注意，这种非局部的返回只支持传给内联函数的 lambda 表达式。) 如果我们需要从 lambda 表达式中返回，我们必须给它加标签并用以限制 return。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">foo</span>() {
    ints.forEach lit<span style="color:#960050;background-color:#1e0010">@</span> {
        <span style="color:#66d9ef">if</span> (<span style="color:#66d9ef">it</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>) <span style="color:#66d9ef">return</span><span style="color:#a6e22e">@lit</span>
        print(<span style="color:#66d9ef">it</span>)
    }
}
</code></pre></div><p>现在，它只会从 lambda 表达式中返回。通常情况下使用隐式标签更方便。 该标签与接受该 lambda 的函数同名。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">foo</span>() {
    ints.forEach {
        <span style="color:#66d9ef">if</span> (<span style="color:#66d9ef">it</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>) <span style="color:#66d9ef">return</span><span style="color:#a6e22e">@forEach</span>
        print(<span style="color:#66d9ef">it</span>)
    }
}
</code></pre></div><p>或者，我们用一个匿名函数替代 lambda 表达式。 匿名函数内部的 return 语句将从该匿名函数自身返回</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">foo</span>() {
    ints.forEach(<span style="color:#66d9ef">fun</span>(value: Int) {
        <span style="color:#66d9ef">if</span> (value <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>) <span style="color:#66d9ef">return</span>
        print(value)
    })
}
</code></pre></div><p>当要返一个回值的时候，解析器优先选用标签限制的 return，即</p>
<pre><code>return@a 1
</code></pre><p>意为&quot;从标签 @a 返回 1&rdquo;，而不是&quot;返回一个标签标注的表达式 (@a 1)&quot;。</p>
<h1 id="kotlin-类和对象">Kotlin 类和对象</h1>
<h2 id="类定义">类定义</h2>
<p>Kotlin 类可以包含：构造函数和初始化代码块、函数、属性、内部类、对象声明。</p>
<p>Kotlin 中使用关键字 <strong>class</strong> 声明类，后面紧跟类名：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Breeze</span> {  <span style="color:#75715e">// 类名为 Breeze
</span><span style="color:#75715e"></span>    <span style="color:#75715e">// 大括号内是类体构成
</span><span style="color:#75715e"></span>}
</code></pre></div><p>我们也可以定义一个空类：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Empty</span>
</code></pre></div><p>可以在类中定义成员函数：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Breeze</span>() {
    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">foo</span>() { print(<span style="color:#e6db74">&#34;Foo&#34;</span>) } <span style="color:#75715e">// 成员函数
</span><span style="color:#75715e"></span>}
</code></pre></div><hr>
<h2 id="类的属性">类的属性</h2>
<h3 id="属性定义">属性定义</h3>
<p>类的属性可以用关键字 <strong>var</strong> 声明为可变的，否则使用只读关键字 <strong>val</strong> 声明为不可变。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Breeze</span> {
    <span style="color:#66d9ef">var</span> name: String = <span style="color:#960050;background-color:#1e0010">……</span>
    <span style="color:#66d9ef">var</span> url: String = <span style="color:#960050;background-color:#1e0010">……</span>
    <span style="color:#66d9ef">var</span> city: String = <span style="color:#960050;background-color:#1e0010">……</span>
}
</code></pre></div><p>我们可以像使用普通函数那样使用构造函数创建类实例：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">val</span> site = Breeze() <span style="color:#75715e">// Kotlin 中没有 new 关键字
</span></code></pre></div><p>要使用一个属性，只要用名称引用它即可</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">site.name           <span style="color:#75715e">// 使用 . 号来引用
</span><span style="color:#75715e"></span>site.url
</code></pre></div><p>Koltin 中的类可以有一个 主构造器，以及一个或多个次构造器，主构造器是类头部的一部分，位于类名称之后:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Person</span> <span style="color:#66d9ef">constructor</span>(firstName: String) {}
</code></pre></div><p>如果主构造器没有任何注解，也没有任何可见度修饰符，那么constructor关键字可以省略。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Person</span>(firstName: String) {
}
</code></pre></div><h3 id="getter-和-setter">getter 和 setter</h3>
<p>属性声明的完整语法：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">var</span> <span style="color:#960050;background-color:#1e0010">&lt;</span>propertyName&gt;[: &lt;PropertyType&gt;] [= &lt;property_initializer&gt;]
<span style="color:#a6e22e">    [&lt;getter&gt;]</span>
<span style="color:#a6e22e">    [&lt;setter&gt;]</span>
 
</code></pre></div><p>getter 和 setter 都是可选</p>
<p>如果属性类型可以从初始化语句或者类的成员函数中推断出来，那就可以省去类型，val不允许设置setter函数，因为它是只读的。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">var</span> allByDefault: Int? <span style="color:#75715e">// 错误: 需要一个初始化语句, 默认实现了 getter 和 setter 方法
</span><span style="color:#75715e"></span><span style="color:#66d9ef">var</span> initialized = <span style="color:#ae81ff">1</span>    <span style="color:#75715e">// 类型为 Int, 默认实现了 getter 和 setter
</span><span style="color:#75715e"></span><span style="color:#66d9ef">val</span> simple: Int?       <span style="color:#75715e">// 类型为 Int ，默认实现 getter ，但必须在构造函数中初始化
</span><span style="color:#75715e"></span><span style="color:#66d9ef">val</span> inferredType = <span style="color:#ae81ff">1</span>   <span style="color:#75715e">// 类型为 Int 类型,默认实现 getter
</span></code></pre></div><h3 id="实例-4">实例</h3>
<p>以下实例定义了一个 Person 类，包含两个可变变量 lastName 和 no，lastName 修改了 getter 方法，no 修改了 setter 方法。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Person</span> {

    <span style="color:#66d9ef">var</span> lastName: String = <span style="color:#e6db74">&#34;zhang&#34;</span>
        <span style="color:#66d9ef">get</span>() = <span style="color:#66d9ef">field</span>.toUpperCase()   <span style="color:#75715e">// 将变量赋值后转换为大写
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">set</span>

    <span style="color:#66d9ef">var</span> no: Int = <span style="color:#ae81ff">100</span>
        <span style="color:#66d9ef">get</span>() = <span style="color:#66d9ef">field</span>                <span style="color:#75715e">// 后端变量
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">set</span>(value) {
            <span style="color:#66d9ef">if</span> (value &lt; <span style="color:#ae81ff">10</span>) {       <span style="color:#75715e">// 如果传入的值小于 10 返回该值
</span><span style="color:#75715e"></span>                <span style="color:#66d9ef">field</span> = value
            } <span style="color:#66d9ef">else</span> {
                <span style="color:#66d9ef">field</span> = -<span style="color:#ae81ff">1</span>         <span style="color:#75715e">// 如果传入的值大于等于 10 返回 -1
</span><span style="color:#75715e"></span>            }
        }

    <span style="color:#66d9ef">var</span> heiht: Float = <span style="color:#ae81ff">145.4f</span>
        <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">set</span>
}

<span style="color:#75715e">// 测试
</span><span style="color:#75715e"></span><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">var</span> person: Person = Person()

    person.lastName = <span style="color:#e6db74">&#34;wang&#34;</span>

    println(<span style="color:#e6db74">&#34;lastName:</span><span style="color:#e6db74">${person.lastName}</span><span style="color:#e6db74">&#34;</span>)

    person.no = <span style="color:#ae81ff">9</span>
    println(<span style="color:#e6db74">&#34;no:</span><span style="color:#e6db74">${person.no}</span><span style="color:#e6db74">&#34;</span>)

    person.no = <span style="color:#ae81ff">20</span>
    println(<span style="color:#e6db74">&#34;no:</span><span style="color:#e6db74">${person.no}</span><span style="color:#e6db74">&#34;</span>)

}
</code></pre></div><p>输出结果为：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">lastName:WANG
no:<span style="color:#ae81ff">9</span>
no:-<span style="color:#ae81ff">1</span>
</code></pre></div><p>Kotlin 中类不能有字段。提供了 Backing Fields(后端变量) 机制,备用字段使用field关键字声明,field 关键词只能用于属性的访问器，如以上实例：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">var</span> no: Int = <span style="color:#ae81ff">100</span>
        <span style="color:#66d9ef">get</span>() = <span style="color:#66d9ef">field</span>                <span style="color:#75715e">// 后端变量
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">set</span>(value) {
            <span style="color:#66d9ef">if</span> (value &lt; <span style="color:#ae81ff">10</span>) {       <span style="color:#75715e">// 如果传入的值小于 10 返回该值
</span><span style="color:#75715e"></span>                <span style="color:#66d9ef">field</span> = value
            } <span style="color:#66d9ef">else</span> {
                <span style="color:#66d9ef">field</span> = -<span style="color:#ae81ff">1</span>         <span style="color:#75715e">// 如果传入的值大于等于 10 返回 -1
</span><span style="color:#75715e"></span>            }
        }
</code></pre></div><p>非空属性必须在定义的时候初始化,kotlin提供了一种可以延迟初始化的方案,使用 <strong>lateinit</strong> 关键字描述属性：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyTest</span> {
    <span style="color:#66d9ef">lateinit</span> <span style="color:#66d9ef">var</span> subject: TestSubject

    <span style="color:#a6e22e">@SetUp</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">setup</span>() {
        subject = TestSubject()
    }

    <span style="color:#a6e22e">@Test</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">test</span>() {
        subject.method()  <span style="color:#75715e">// dereference directly
</span><span style="color:#75715e"></span>    }
}
</code></pre></div><hr>
<h2 id="主构造器">主构造器</h2>
<p>主构造器中不能包含任何代码，初始化代码可以放在初始化代码段中，初始化代码段使用 init 关键字作为前缀。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Person</span> <span style="color:#66d9ef">constructor</span>(firstName: String) {
    <span style="color:#66d9ef">init</span> {
        println(<span style="color:#e6db74">&#34;FirstName is </span><span style="color:#e6db74">$firstName</span><span style="color:#e6db74">&#34;</span>)
    }
}
</code></pre></div><p>注意：主构造器的参数可以在初始化代码段中使用，也可以在类主体n定义的属性初始化代码中使用。 一种简洁语法，可以通过主构造器来定义属性并初始化属性值(可以是var或val)：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">People</span>(<span style="color:#66d9ef">val</span> firstName: String, <span style="color:#66d9ef">val</span> lastName: String) {
    <span style="color:#75715e">//...
</span><span style="color:#75715e"></span>}
</code></pre></div><p>如果构造器有注解，或者有可见度修饰符，这时constructor关键字是必须的，注解和修饰符要放在它之前。</p>
<h3 id="实例-5">实例</h3>
<p>创建一个 Breeze类，并通过构造函数传入网站名：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Breeze</span>  <span style="color:#66d9ef">constructor</span>(name: String) {  <span style="color:#75715e">// 类名为 Breeze
</span><span style="color:#75715e"></span>    <span style="color:#75715e">// 大括号内是类体构成
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">var</span> url: String = <span style="color:#e6db74">&#34;http://www.Breeze.com&#34;</span>
    <span style="color:#66d9ef">var</span> country: String = <span style="color:#e6db74">&#34;CN&#34;</span>
    <span style="color:#66d9ef">var</span> siteName = name

    <span style="color:#66d9ef">init</span> {
        println(<span style="color:#e6db74">&#34;初始化网站名: </span><span style="color:#e6db74">${name}</span><span style="color:#e6db74">&#34;</span>)
    }

    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">printTest</span>() {
        println(<span style="color:#e6db74">&#34;我是类的函数&#34;</span>)
    }
}

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> Breeze =  Breeze(<span style="color:#e6db74">&#34;菜鸟教程&#34;</span>)
    println(Breeze.siteName)
    println(Breeze.url)
    println(Breeze.country)
    Breeze.printTest()
}
</code></pre></div><p>输出结果为：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#960050;background-color:#1e0010">初始化网站名</span>: <span style="color:#960050;background-color:#1e0010">菜鸟教程</span>
<span style="color:#960050;background-color:#1e0010">菜鸟教程</span>
http:<span style="color:#75715e">//www.Breeze.com
</span><span style="color:#75715e"></span>CN
<span style="color:#960050;background-color:#1e0010">我是类的函数</span>
</code></pre></div><hr>
<h2 id="次构造函数">次构造函数</h2>
<p>类也可以有二级构造函数，需要加前缀 constructor:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Person</span> { 
    <span style="color:#66d9ef">constructor</span>(parent: Person) {
        parent.children.add(<span style="color:#66d9ef">this</span>) 
    }
}
</code></pre></div><p>如果类有主构造函数，每个次构造函数都要，或直接或间接通过另一个次构造函数代理主构造函数。在同一个类中代理另一个构造函数使用 this 关键字：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Person</span>(<span style="color:#66d9ef">val</span> name: String) {
    <span style="color:#66d9ef">constructor</span> (name: String, age:Int) : <span style="color:#66d9ef">this</span>(name) {
        <span style="color:#75715e">// 初始化...
</span><span style="color:#75715e"></span>    }
}
</code></pre></div><p>如果一个非抽象类没有声明构造函数(主构造函数或次构造函数)，它会产生一个没有参数的构造函数。构造函数是 public 。如果你不想你的类有公共的构造函数，你就得声明一个空的主构造函数：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">DontCreateMe</span> <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">constructor</span> () {
}
</code></pre></div><blockquote>
<p>注意：在 JVM 虚拟机中，如果主构造函数的所有参数都有默认值，编译器会生成一个附加的无参的构造函数，这个构造函数会直接使用默认值。这使得 Kotlin 可以更简单的使用像 Jackson 或者 JPA 这样使用无参构造函数来创建类实例的库。</p>
<pre><code>class Customer(val customerName: String = &quot;&quot;)
</code></pre></blockquote>
<h3 id="实例-6">实例</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Breeze</span>  <span style="color:#66d9ef">constructor</span>(name: String) {  <span style="color:#75715e">// 类名为 Breeze
</span><span style="color:#75715e"></span>    <span style="color:#75715e">// 大括号内是类体构成
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">var</span> url: String = <span style="color:#e6db74">&#34;http://www.Breeze.com&#34;</span>
    <span style="color:#66d9ef">var</span> country: String = <span style="color:#e6db74">&#34;CN&#34;</span>
    <span style="color:#66d9ef">var</span> siteName = name

    <span style="color:#66d9ef">init</span> {
        println(<span style="color:#e6db74">&#34;初始化网站名: </span><span style="color:#e6db74">${name}</span><span style="color:#e6db74">&#34;</span>)
    }
    <span style="color:#75715e">// 次构造函数
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">constructor</span> (name: String, alexa: Int) : <span style="color:#66d9ef">this</span>(name) {
        println(<span style="color:#e6db74">&#34;Alexa 排名 </span><span style="color:#e6db74">$alexa</span><span style="color:#e6db74">&#34;</span>)
    }

    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">printTest</span>() {
        println(<span style="color:#e6db74">&#34;我是类的函数&#34;</span>)
    }
}

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> Breeze =  Breeze(<span style="color:#e6db74">&#34;菜鸟教程&#34;</span>, <span style="color:#ae81ff">10000</span>)
    println(Breeze.siteName)
    println(Breeze.url)
    println(Breeze.country)
    Breeze.printTest()
}
</code></pre></div><p>输出结果为：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#960050;background-color:#1e0010">初始化网站名</span>: <span style="color:#960050;background-color:#1e0010">菜鸟教程</span>
Alexa <span style="color:#960050;background-color:#1e0010">排名</span> <span style="color:#ae81ff">10000</span>
<span style="color:#960050;background-color:#1e0010">菜鸟教程</span>
http:<span style="color:#75715e">//www.Breeze.com
</span><span style="color:#75715e"></span>CN
<span style="color:#960050;background-color:#1e0010">我是类的函数</span>
</code></pre></div><hr>
<h2 id="抽象类">抽象类</h2>
<p>抽象是面向对象编程的特征之一，类本身，或类中的部分成员，都可以声明为abstract的。抽象成员在类中不存在具体的实现。</p>
<p>注意：无需对抽象类或抽象成员标注open注解。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">open</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Base</span> {
    <span style="color:#66d9ef">open</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">f</span>() {}
}

<span style="color:#66d9ef">abstract</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Derived</span> : Base() {
    <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">abstract</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">f</span>()
}
</code></pre></div><hr>
<h2 id="嵌套类">嵌套类</h2>
<p>我们可以把类嵌套在其他类中，看以下实例：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Outer</span> {                  <span style="color:#75715e">// 外部类
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">val</span> bar: Int = <span style="color:#ae81ff">1</span>
    <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Nested</span> {             <span style="color:#75715e">// 嵌套类
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">foo</span>() = <span style="color:#ae81ff">2</span>
    }
}

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> demo = Outer.Nested().foo() <span style="color:#75715e">// 调用格式：外部类.嵌套类.嵌套类方法/属性
</span><span style="color:#75715e"></span>    println(demo)    <span style="color:#75715e">// == 2
</span><span style="color:#75715e"></span>}
</code></pre></div><hr>
<h2 id="内部类">内部类</h2>
<p>内部类使用 inner 关键字来表示。</p>
<p>内部类会带有一个对外部类的对象的引用，所以内部类可以访问外部类成员属性和成员函数。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Outer</span> {
    <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">val</span> bar: Int = <span style="color:#ae81ff">1</span>
    <span style="color:#66d9ef">var</span> v = <span style="color:#e6db74">&#34;成员属性&#34;</span>
    <span style="color:#75715e">/**嵌套内部类**/</span>
    <span style="color:#66d9ef">inner</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Inner</span> {
        <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">foo</span>() = bar  <span style="color:#75715e">// 访问外部类成员
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">innerTest</span>() {
            <span style="color:#66d9ef">var</span> o = <span style="color:#66d9ef">this</span><span style="color:#a6e22e">@Outer</span> <span style="color:#75715e">//获取外部类的成员变量
</span><span style="color:#75715e"></span>            println(<span style="color:#e6db74">&#34;内部类可以引用外部类的成员，例如：&#34;</span> + o.v)
        }
    }
}

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> demo = Outer().Inner().foo()
    println(demo) <span style="color:#75715e">//   1
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">val</span> demo2 = Outer().Inner().innerTest()   
    println(demo2)   <span style="color:#75715e">// 内部类可以引用外部类的成员，例如：成员属性
</span><span style="color:#75715e"></span>}
</code></pre></div><p>为了消除歧义，要访问来自外部作用域的 this，我们使用this@label，其中 @label 是一个 代指 this 来源的标签。</p>
<hr>
<h2 id="匿名内部类">匿名内部类</h2>
<p>使用对象表达式来创建匿名内部类：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Test</span> {
    <span style="color:#66d9ef">var</span> v = <span style="color:#e6db74">&#34;成员属性&#34;</span>

    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">setInterFace</span>(test: TestInterFace) {
        test.test()
    }
}

<span style="color:#75715e">/**
</span><span style="color:#75715e"> * 定义接口
</span><span style="color:#75715e"> */</span>
<span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">TestInterFace</span> {
    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">test</span>()
}

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">var</span> test = Test()

    <span style="color:#75715e">/**
</span><span style="color:#75715e">     * 采用对象表达式来创建接口对象，即匿名内部类的实例。
</span><span style="color:#75715e">     */</span>
    test.setInterFace(<span style="color:#66d9ef">object</span> <span style="color:#960050;background-color:#1e0010">: </span><span style="color:#a6e22e">TestInterFace</span> {
        <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">test</span>() {
            println(<span style="color:#e6db74">&#34;对象表达式创建匿名内部类的实例&#34;</span>)
        }
    })
}
</code></pre></div><hr>
<h2 id="类的修饰符">类的修饰符</h2>
<p>类的修饰符包括 classModifier 和_accessModifier_:</p>
<ul>
<li>
<p>classModifier: 类属性修饰符，标示类本身特性。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">abstract</span>    <span style="color:#75715e">// 抽象类  
</span><span style="color:#75715e"></span><span style="color:#66d9ef">final</span>       <span style="color:#75715e">// 类不可继承，默认属性
</span><span style="color:#75715e"></span><span style="color:#66d9ef">enum</span>        <span style="color:#75715e">// 枚举类
</span><span style="color:#75715e"></span><span style="color:#66d9ef">open</span>        <span style="color:#75715e">// 类可继承，类默认是final的
</span><span style="color:#75715e"></span><span style="color:#66d9ef">annotation</span>  <span style="color:#75715e">// 注解类
</span></code></pre></div></li>
<li>
<p>accessModifier: 访问权限修饰符</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">private</span>    <span style="color:#75715e">// 仅在同一个文件中可见
</span><span style="color:#75715e"></span><span style="color:#66d9ef">protected</span>  <span style="color:#75715e">// 同一个文件中或子类可见
</span><span style="color:#75715e"></span><span style="color:#66d9ef">public</span>     <span style="color:#75715e">// 所有调用的地方都可见
</span><span style="color:#75715e"></span><span style="color:#66d9ef">internal</span>   <span style="color:#75715e">// 同一个模块中可见
</span></code></pre></div></li>
</ul>
<h3 id="实例-7">实例</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#75715e">// 文件名：example.kt
</span><span style="color:#75715e"></span><span style="color:#66d9ef">package</span> foo

<span style="color:#66d9ef">private</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">foo</span>() {} <span style="color:#75715e">// 在 example.kt 内可见
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">public</span> <span style="color:#66d9ef">var</span> bar: Int = <span style="color:#ae81ff">5</span> <span style="color:#75715e">// 该属性随处可见
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">internal</span> <span style="color:#66d9ef">val</span> baz = <span style="color:#ae81ff">6</span>    <span style="color:#75715e">// 相同模块内可见
</span></code></pre></div><h1 id="kotlin-继承">Kotlin 继承</h1>
<p>Kotlin 中所有类都继承该 Any 类，它是所有类的超类，对于没有超类型声明的类是默认超类：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Example</span> <span style="color:#75715e">// 从 Any 隐式继承
</span></code></pre></div><p>Any 默认提供了三个函数：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">equals()

hashCode()

toString()
</code></pre></div><p>注意：Any 不是 java.lang.Object。</p>
<p>如果一个类要被继承，可以使用 open 关键字进行修饰。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">open</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Base</span>(p: Int)           <span style="color:#75715e">// 定义基类
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Derived</span>(p: Int) : Base(p)
</code></pre></div><hr>
<h2 id="构造函数">构造函数</h2>
<h3 id="子类有主构造函数">子类有主构造函数</h3>
<p>如果子类有主构造函数， 则基类必须在主构造函数中立即初始化。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">open</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Person</span>(<span style="color:#66d9ef">var</span> name : String, <span style="color:#66d9ef">var</span> age : Int){<span style="color:#75715e">// 基类
</span><span style="color:#75715e"></span>
}

<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Student</span>(name : String, age : Int, <span style="color:#66d9ef">var</span> no : String, <span style="color:#66d9ef">var</span> score : Int) : Person(name, age) {

}

<span style="color:#75715e">// 测试
</span><span style="color:#75715e"></span><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> s =  Student(<span style="color:#e6db74">&#34;Breeze&#34;</span>, <span style="color:#ae81ff">18</span>, <span style="color:#e6db74">&#34;S12346&#34;</span>, <span style="color:#ae81ff">89</span>)
    println(<span style="color:#e6db74">&#34;学生名： </span><span style="color:#e6db74">${s.name}</span><span style="color:#e6db74">&#34;</span>)
    println(<span style="color:#e6db74">&#34;年龄： </span><span style="color:#e6db74">${s.age}</span><span style="color:#e6db74">&#34;</span>)
    println(<span style="color:#e6db74">&#34;学生号： </span><span style="color:#e6db74">${s.no}</span><span style="color:#e6db74">&#34;</span>)
    println(<span style="color:#e6db74">&#34;成绩： </span><span style="color:#e6db74">${s.score}</span><span style="color:#e6db74">&#34;</span>)
}
</code></pre></div><p>输出结果：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#960050;background-color:#1e0010">学生名：</span> Breeze
<span style="color:#960050;background-color:#1e0010">年龄：</span> <span style="color:#ae81ff">18</span>
<span style="color:#960050;background-color:#1e0010">学生号：</span> S12346
<span style="color:#960050;background-color:#1e0010">成绩：</span> <span style="color:#ae81ff">89</span>
</code></pre></div><h3 id="子类没有主构造函数">子类没有主构造函数</h3>
<p>如果子类没有主构造函数，则必须在每一个二级构造函数中用 super 关键字初始化基类，或者在代理另一个构造函数。初始化基类时，可以调用基类的不同构造方法。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Student</span> : Person {

    <span style="color:#66d9ef">constructor</span>(ctx: Context) : <span style="color:#66d9ef">super</span>(ctx) {
    } 

    <span style="color:#66d9ef">constructor</span>(ctx: Context, attrs: AttributeSet) : <span style="color:#66d9ef">super</span>(ctx,attrs) {
    }
}
</code></pre></div><h3 id="实例-8">实例</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#75715e">/**用户基类**/</span>
<span style="color:#66d9ef">open</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Person</span>(name:String){
    <span style="color:#75715e">/**次级构造函数**/</span>
    <span style="color:#66d9ef">constructor</span>(name:String,age:Int):<span style="color:#66d9ef">this</span>(name){
        <span style="color:#75715e">//初始化
</span><span style="color:#75715e"></span>        println(<span style="color:#e6db74">&#34;-------基类次级构造函数---------&#34;</span>)
    }
}

<span style="color:#75715e">/**子类继承 Person 类**/</span>
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Student</span>:Person{

    <span style="color:#75715e">/**次级构造函数**/</span>
    <span style="color:#66d9ef">constructor</span>(name:String,age:Int,no:String,score:Int):<span style="color:#66d9ef">super</span>(name,age){
        println(<span style="color:#e6db74">&#34;-------继承类次级构造函数---------&#34;</span>)
        println(<span style="color:#e6db74">&#34;学生名： </span><span style="color:#e6db74">${name}</span><span style="color:#e6db74">&#34;</span>)
        println(<span style="color:#e6db74">&#34;年龄： </span><span style="color:#e6db74">${age}</span><span style="color:#e6db74">&#34;</span>)
        println(<span style="color:#e6db74">&#34;学生号： </span><span style="color:#e6db74">${no}</span><span style="color:#e6db74">&#34;</span>)
        println(<span style="color:#e6db74">&#34;成绩： </span><span style="color:#e6db74">${score}</span><span style="color:#e6db74">&#34;</span>)
    }
}

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">var</span> s =  Student(<span style="color:#e6db74">&#34;Breeze&#34;</span>, <span style="color:#ae81ff">18</span>, <span style="color:#e6db74">&#34;S12345&#34;</span>, <span style="color:#ae81ff">89</span>)
}
</code></pre></div><p>输出结果：</p>
<pre><code>-------基类次级构造函数---------
-------继承类次级构造函数---------
学生名： Breeze
年龄： 18
学生号： S12345
成绩： 89
</code></pre><hr>
<h2 id="重写">重写</h2>
<p>在基类中，使用fun声明函数时，此函数默认为final修饰，不能被子类重写。如果允许子类重写该函数，那么就要手动添加 open 修饰它, 子类重写方法使用 override 关键词：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#75715e">/**用户基类**/</span>
<span style="color:#66d9ef">open</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Person</span>{
    <span style="color:#66d9ef">open</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">study</span>(){       <span style="color:#75715e">// 允许子类重写
</span><span style="color:#75715e"></span>        println(<span style="color:#e6db74">&#34;我毕业了&#34;</span>)
    }
}

<span style="color:#75715e">/**子类继承 Person 类**/</span>
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Student</span> : Person() {

    <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">study</span>(){    <span style="color:#75715e">// 重写方法
</span><span style="color:#75715e"></span>        println(<span style="color:#e6db74">&#34;我在读大学&#34;</span>)
    }
}

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> s =  Student()
    s.study();

}
</code></pre></div><p>输出结果为:</p>
<pre><code>我在读大学
</code></pre><p>如果有多个相同的方法(继承或者实现自其他类，如A、B类)，则必须要重写该方法，使用super范型去选择性地调用父类的实现。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">open</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">A</span> {
    <span style="color:#66d9ef">open</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">f</span> () { print(<span style="color:#e6db74">&#34;A&#34;</span>) }
    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">a</span>() { print(<span style="color:#e6db74">&#34;a&#34;</span>) }
}

<span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">B</span> {
    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">f</span>() { print(<span style="color:#e6db74">&#34;B&#34;</span>) } <span style="color:#75715e">//接口的成员变量默认是 open 的
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">b</span>() { print(<span style="color:#e6db74">&#34;b&#34;</span>) }
}

<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">C</span>() : A() , B{
    <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">f</span>() {
        <span style="color:#66d9ef">super</span>&lt;A&gt;.f()<span style="color:#75715e">//调用 A.f()
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">super</span>&lt;B&gt;.f()<span style="color:#75715e">//调用 B.f()
</span><span style="color:#75715e"></span>    }
}

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> c =  C()
    c.f();

}
</code></pre></div><p>C 继承自 a() 或 b(), C 不仅可以从 A 或则 B 中继承函数，而且 C 可以继承 A()、B() 中共有的函数。此时该函数在中只有一个实现，为了消除歧义，该函数必须调用A()和B()中该函数的实现，并提供自己的实现。</p>
<p>输出结果为:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">AB
</code></pre></div><hr>
<h2 id="属性重写">属性重写</h2>
<p>属性重写使用 override 关键字，属性必须具有兼容类型，每一个声明的属性都可以通过初始化程序或者getter方法被重写：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">open</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Foo</span> {
    <span style="color:#66d9ef">open</span> <span style="color:#66d9ef">val</span> x: Int <span style="color:#66d9ef">get</span> { <span style="color:#960050;background-color:#1e0010">……</span> }
}

<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Bar1</span> : Foo() {
    <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">val</span> x: Int = <span style="color:#960050;background-color:#1e0010">……</span>
}
</code></pre></div><p>你可以用一个var属性重写一个val属性，但是反过来不行。因为val属性本身定义了getter方法，重写为var属性会在衍生类中额外声明一个setter方法</p>
<p>你可以在主构造函数中使用 override 关键字作为属性声明的一部分:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">Foo</span> {
    <span style="color:#66d9ef">val</span> count: Int
}

<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Bar1</span>(<span style="color:#66d9ef">override</span> <span style="color:#66d9ef">val</span> count: Int) : Foo

<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Bar2</span> : Foo {
    <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">var</span> count: Int = <span style="color:#ae81ff">0</span>
}
</code></pre></div><h2 id="几点补充"><strong>几点补充：</strong></h2>
<p>1、子类继承父类时，不能有跟父类同名的变量，除非父类中该变量为 private，或者父类中该变量为 open 并且子类用 override 关键字重写:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">open</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Person</span>(<span style="color:#66d9ef">var</span> name: String, <span style="color:#66d9ef">var</span> age: Int) {    
    <span style="color:#66d9ef">open</span> <span style="color:#66d9ef">var</span> sex: String = <span style="color:#e6db74">&#34;unknow&#34;</span>    
    <span style="color:#66d9ef">init</span> {        
        println(<span style="color:#e6db74">&#34;基类初始化&#34;</span>)    
    }
}
<span style="color:#75715e">// 子类的主构造方法的 name 前边也加了 var，这是不允许的，会报&#39;name&#39; hides member of supertype and needs &#39;override&#39; modifier
</span><span style="color:#75715e"></span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Student</span>(<span style="color:#66d9ef">var</span> name: String, age: Int, <span style="color:#66d9ef">var</span> no: String, <span style="color:#66d9ef">var</span> score: Int) : Person(name, age) {
    <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">var</span> sex: String = <span style="color:#e6db74">&#34;male&#34;</span>
}
</code></pre></div><p>如上代码片段中，子类 Student 主构造方法的第一个字段 name 前边加 var 关键字会报错。</p>
<p>2、子类不一定要调用父类和接口中共同拥有的同名的方法</p>
<p>引用文章中的话：“C 继承自 a() 或 b(), C 不仅可以从 A 或则 B 中继承函数，而且 C 可以继承 A()、B() 中共有的函数。此时该函数在中只有一个实现，为了消除歧义，该函数必须调用A()和B()中该函数的实现，并提供自己的实现。”</p>
<p>我试过了，不是必须调用 A() 和 B() 中该函数的实现，代码如下：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">open</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">A</span> {
    <span style="color:#66d9ef">open</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">f</span>() {
        println(<span style="color:#e6db74">&#34;A&#34;</span>)
    }
    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">a</span>() {
        println(<span style="color:#e6db74">&#34;a&#34;</span>)
    }
}
<span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">B</span> {
    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">f</span>() {
        println(<span style="color:#e6db74">&#34;B&#34;</span>)
    }
    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">b</span>() {
        println(<span style="color:#e6db74">&#34;b&#34;</span>)
    }
}
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">C</span> : A(), B {
    <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">f</span>() {
        <span style="color:#75715e">// super&lt;A&gt;.f()
</span><span style="color:#75715e"></span>        <span style="color:#75715e">// super&lt;B&gt;.f()
</span><span style="color:#75715e"></span>        println(<span style="color:#e6db74">&#34;C&#34;</span>)
    }
}
</code></pre></div><p>如上代码片段，注释掉 <strong>super<!-- raw HTML omitted -->.f()</strong> 和 <strong>super<!-- raw HTML omitted -->.f()</strong> 也不报错。</p>
<p>3、关于子类不能用 val 重写父类中的 var，我的猜测是：子类重写父类属性，也就相当于必须重写该属性的 getter 和 setter 方法，而子类中的 val 不能有 setter 方法，所以无法“覆盖”父类中 var 的 setter 方法，相当于缩小了父类中相应属性的使用范围，是不允许的，就像我们不能把父类中一个 public 方法重写成 private 方法一样。</p>
<p>4、如果一个变量想要在定义的时候被初始化，则该变量必须拥有 backing field 字段，该变量的默认 getter 和 setter 方法中是有定义 field 字段的，但是如果我们重写了这个变量的 getter 方法和 setter 方法，并且在 getter 方法和 setter 方法中都没有出现过 filed 这个关键字，则编译器会报错，提示 Initializer is not allowed here because this property has no backing field，除非显式写出 filed 关键字(哪怕它什么都不干，只要放在那里就可以了，我理解是出现一次就相当于“声明”过了，就可以用了，而在定义变量的时候初始化是要求 field 被“声明”过才可以)：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">var</span> aaa: Int = <span style="color:#ae81ff">0</span>
<span style="color:#66d9ef">get</span>() {
    <span style="color:#66d9ef">field</span> <span style="color:#75715e">// 这里必须出现一下field关键字，否则 var aaa: Int = 0 会报错，除非你去掉 = 0这部分，不要给它赋初始化值
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">return</span> <span style="color:#ae81ff">0</span>
}
<span style="color:#66d9ef">set</span>(value) {}
</code></pre></div><h1 id="kotlin-接口">Kotlin 接口</h1>
<p>Kotlin 接口与 Java 8 类似，使用 interface 关键字定义接口，允许方法有默认实现：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">MyInterface</span> {
    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">bar</span>()    <span style="color:#75715e">// 未实现
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">foo</span>() {  <span style="color:#75715e">//已实现
</span><span style="color:#75715e"></span>      <span style="color:#75715e">// 可选的方法体
</span><span style="color:#75715e"></span>      println(<span style="color:#e6db74">&#34;foo&#34;</span>)
    }
}
</code></pre></div><h3 id="实现接口">实现接口</h3>
<p>一个类或者对象可以实现一个或多个接口。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Child</span> : MyInterface {
    <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">bar</span>() {
        <span style="color:#75715e">// 方法体
</span><span style="color:#75715e"></span>    }
}
</code></pre></div><h2 id="实例-9">实例</h2>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">MyInterface</span> {
    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">bar</span>()
    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">foo</span>() {
        <span style="color:#75715e">// 可选的方法体
</span><span style="color:#75715e"></span>        println(<span style="color:#e6db74">&#34;foo&#34;</span>)
    }
}
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Child</span> : MyInterface {
    <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">bar</span>() {
        <span style="color:#75715e">// 方法体
</span><span style="color:#75715e"></span>        println(<span style="color:#e6db74">&#34;bar&#34;</span>)
    }
}
<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> c =  Child()
    c.foo();
    c.bar();
 
}
</code></pre></div><p>输出结果为：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">foo
bar
</code></pre></div><h3 id="接口中的属性">接口中的属性</h3>
<p>接口中的属性只能是抽象的，不允许初始化值，接口不会保存属性值，实现接口时，必须重写属性：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">MyInterface</span>{
    <span style="color:#66d9ef">var</span> name:String <span style="color:#75715e">//name 属性, 抽象的
</span><span style="color:#75715e"></span>}
 
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyImpl</span>:MyInterface{
    <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">var</span> name: String = <span style="color:#e6db74">&#34;luckly&#34;</span> <span style="color:#75715e">//重写属性
</span><span style="color:#75715e"></span>}
</code></pre></div><h2 id="实例-10">实例</h2>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">MyInterface</span> {
    <span style="color:#66d9ef">var</span> name:String <span style="color:#75715e">//name 属性, 抽象的
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">bar</span>()
    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">foo</span>() {
        <span style="color:#75715e">// 可选的方法体
</span><span style="color:#75715e"></span>        println(<span style="color:#e6db74">&#34;foo&#34;</span>)
    }
}
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Child</span> : MyInterface {
    <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">var</span> name: String = <span style="color:#e6db74">&#34;breeze&#34;</span> <span style="color:#75715e">//重写属性
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">bar</span>() {
        <span style="color:#75715e">// 方法体
</span><span style="color:#75715e"></span>        println(<span style="color:#e6db74">&#34;bar&#34;</span>)
    }
}
<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> c =  Child()
    c.foo();
    c.bar();
    println(c.name)
 
}
</code></pre></div><p>输出结果为：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">foo
bar
breeze
</code></pre></div><h1 id="kotlin-扩展">Kotlin 扩展</h1>
<p>Kotlin 可以对一个类的属性和方法进行扩展，且不需要继承或使用 Decorator 模式。</p>
<p>扩展是一种静态行为，对被扩展的类代码本身不会造成任何影响。</p>
<hr>
<h2 id="扩展函数">扩展函数</h2>
<p>扩展函数可以在已有类中添加新的方法，不会对原类做修改，扩展函数定义形式：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">receiverType</span>.functionName(params){
    body
}
</code></pre></div><ul>
<li>receiverType：表示函数的接收者，也就是函数扩展的对象</li>
<li>functionName：扩展函数的名称</li>
<li>params：扩展函数的参数，可以为NULL</li>
</ul>
<p>以下实例扩展 User 类 ：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">User</span>(<span style="color:#66d9ef">var</span> name:String)

<span style="color:#75715e">/**扩展函数**/</span>
<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">User</span>.Print(){
    print(<span style="color:#e6db74">&#34;用户名 </span><span style="color:#e6db74">$name</span><span style="color:#e6db74">&#34;</span>)
}

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(arg:Array&lt;String&gt;){
    <span style="color:#66d9ef">var</span> user = User(<span style="color:#e6db74">&#34;Breeze&#34;</span>)
    user.Print()
}
</code></pre></div><p>实例执行输出结果为：</p>
<pre><code>用户名 Breeze
</code></pre><p>下面代码为 MutableList 添加一个swap 函数：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#75715e">// 扩展函数 swap,调换不同位置的值
</span><span style="color:#75715e"></span><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">MutableList</span>&lt;Int&gt;.swap(index1: Int, index2: Int) {
    <span style="color:#66d9ef">val</span> tmp = <span style="color:#66d9ef">this</span>[index1]     <span style="color:#75715e">//  this 对应该列表
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">this</span>[index1] = <span style="color:#66d9ef">this</span>[index2]
    <span style="color:#66d9ef">this</span>[index2] = tmp
}

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {

    <span style="color:#66d9ef">val</span> l = mutableListOf(<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">2</span>, <span style="color:#ae81ff">3</span>)
    <span style="color:#75715e">// 位置 0 和 2 的值做了互换
</span><span style="color:#75715e"></span>    l.swap(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">2</span>) <span style="color:#75715e">// &#39;swap()&#39; 函数内的 &#39;this&#39; 将指向 &#39;l&#39; 的值
</span><span style="color:#75715e"></span>
    println(l.toString())
}
</code></pre></div><p>实例执行输出结果为：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#a6e22e">[3, 2, 1]</span>
</code></pre></div><p>this关键字指代接收者对象(receiver object)(也就是调用扩展函数时, 在点号之前指定的对象实例)。</p>
<hr>
<h2 id="扩展函数是静态解析的">扩展函数是静态解析的</h2>
<p>扩展函数是静态解析的，并不是接收者类型的虚拟成员，在调用扩展函数时，具体被调用的的是哪一个函数，由调用函数的的对象表达式来决定的，而不是动态的类型决定的:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">open</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">C</span>

<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">D</span>: C()

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">C</span>.foo() = <span style="color:#e6db74">&#34;c&#34;</span>   <span style="color:#75715e">// 扩展函数 foo
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">D</span>.foo() = <span style="color:#e6db74">&#34;d&#34;</span>   <span style="color:#75715e">// 扩展函数 foo
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">printFoo</span>(c: C) {
    println(c.foo())  <span style="color:#75715e">// 类型是 C 类
</span><span style="color:#75715e"></span>}

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(arg:Array&lt;String&gt;){
    printFoo(D())
}
</code></pre></div><p>实例执行输出结果为：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">c
</code></pre></div><p>若扩展函数和成员函数一致，则使用该函数时，会优先使用成员函数。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">C</span> {
    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">foo</span>() { println(<span style="color:#e6db74">&#34;成员函数&#34;</span>) }
}

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">C</span>.foo() { println(<span style="color:#e6db74">&#34;扩展函数&#34;</span>) }

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(arg:Array&lt;String&gt;){
    <span style="color:#66d9ef">var</span> c = C()
    c.foo()
}
</code></pre></div><p>实例执行输出结果为：</p>
<pre><code>成员函数
</code></pre><h3 id="扩展一个空对象">扩展一个空对象</h3>
<p>在扩展函数内， 可以通过 this 来判断接收者是否为 NULL,这样，即使接收者为 NULL,也可以调用扩展函数。例如:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">Any</span><span style="color:#f92672">?.</span>toString(): String {
    <span style="color:#66d9ef">if</span> (<span style="color:#66d9ef">this</span> <span style="color:#f92672">==</span> <span style="color:#66d9ef">null</span>) <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;null&#34;</span>
    <span style="color:#75715e">// 空检测之后，“this”会自动转换为非空类型，所以下面的 toString()
</span><span style="color:#75715e"></span>    <span style="color:#75715e">// 解析为 Any 类的成员函数
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">return</span> toString()
}
<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(arg:Array&lt;String&gt;){
    <span style="color:#66d9ef">var</span> t = <span style="color:#66d9ef">null</span>
    println(t.toString())
}
</code></pre></div><p>实例执行输出结果为：</p>
<pre><code>null
</code></pre><hr>
<p>扩展属性</p>
<p>除了函数，Kotlin 也支持属性对属性进行扩展:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">val</span> <span style="color:#960050;background-color:#1e0010">&lt;</span>T&gt; List&lt;T&gt;.lastIndex: Int
    <span style="color:#66d9ef">get</span>() = size - <span style="color:#ae81ff">1</span>
 
</code></pre></div><p>扩展属性允许定义在类或者kotlin文件中，不允许定义在函数中。初始化属性因为属性没有后端字段(backing field)，所以不允许被初始化，只能由显式提供的 getter/setter 定义。</p>
<pre><code>val Foo.bar = 1 // 错误：扩展属性不能有初始化器
</code></pre><p>扩展属性只能被声明为 val。</p>
<hr>
<h2 id="伴生对象的扩展">伴生对象的扩展</h2>
<p>如果一个类定义有一个伴生对象 ，你也可以为伴生对象定义扩展函数和属性。</p>
<p>伴生对象通过&quot;类名.&ldquo;形式调用伴生对象，伴生对象声明的扩展函数，通过用类名限定符来调用：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyClass</span> {
    <span style="color:#66d9ef">companion</span> <span style="color:#66d9ef">object</span> { }  <span style="color:#75715e">// 将被称为 &#34;Companion&#34;
</span><span style="color:#75715e"></span>}

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">MyClass</span>.Companion.foo() {
    println(<span style="color:#e6db74">&#34;伴随对象的扩展函数&#34;</span>)
}

<span style="color:#66d9ef">val</span> MyClass.Companion.no: Int
    <span style="color:#66d9ef">get</span>() = <span style="color:#ae81ff">10</span>

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    println(<span style="color:#e6db74">&#34;no:</span><span style="color:#e6db74">${MyClass.no}</span><span style="color:#e6db74">&#34;</span>)
    MyClass.foo()
}
</code></pre></div><p>实例执行输出结果为：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">no:<span style="color:#ae81ff">10</span>
<span style="color:#960050;background-color:#1e0010">伴随对象的扩展函数</span>
</code></pre></div><hr>
<h2 id="扩展的作用域">扩展的作用域</h2>
<p>通常扩展函数或属性定义在顶级包下:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">package</span> foo.bar

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">Baz</span>.goo() { <span style="color:#960050;background-color:#1e0010">……</span> } 
</code></pre></div><p>要使用所定义包之外的一个扩展, 通过import导入扩展的函数名进行使用:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">package</span> com.example.usage

<span style="color:#66d9ef">import</span> foo.bar.goo <span style="color:#75715e">// 导入所有名为 goo 的扩展
</span><span style="color:#75715e"></span>                   <span style="color:#75715e">// 或者
</span><span style="color:#75715e"></span><span style="color:#66d9ef">import</span> foo.bar.*   <span style="color:#75715e">// 从 foo.bar 导入一切
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">usage</span>(baz: Baz) {
    baz.goo()
}
</code></pre></div><hr>
<h2 id="扩展声明为成员">扩展声明为成员</h2>
<p>在一个类内部你可以为另一个类声明扩展。</p>
<p>在这个扩展中，有个多个隐含的接受者，其中扩展方法定义所在类的实例称为分发接受者，而扩展方法的目标类型的实例称为扩展接受者。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">D</span> {
    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">bar</span>() { println(<span style="color:#e6db74">&#34;D bar&#34;</span>) }
}

<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">C</span> {
    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">baz</span>() { println(<span style="color:#e6db74">&#34;C baz&#34;</span>) }

    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">D</span>.foo() {
        bar()   <span style="color:#75715e">// 调用 D.bar
</span><span style="color:#75715e"></span>        baz()   <span style="color:#75715e">// 调用 C.baz
</span><span style="color:#75715e"></span>    }

    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">caller</span>(d: D) {
        d.foo()   <span style="color:#75715e">// 调用扩展函数
</span><span style="color:#75715e"></span>    }
}

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> c: C = C()
    <span style="color:#66d9ef">val</span> d: D = D()
    c.caller(d)

}
</code></pre></div><p>实例执行输出结果为：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">D bar
C baz
</code></pre></div><p>在 C 类内，创建了 D 类的扩展。此时，C 被成为分发接受者，而 D 为扩展接受者。从上例中，可以清楚的看到，在扩展函数中，可以调用派发接收者的成员函数。</p>
<p>假如在调用某一个函数，而该函数在分发接受者和扩展接受者均存在，则以扩展接收者优先，要引用分发接收者的成员你可以使用限定的 this 语法。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">D</span> {
    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">bar</span>() { println(<span style="color:#e6db74">&#34;D bar&#34;</span>) }
}

<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">C</span> {
    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">bar</span>() { println(<span style="color:#e6db74">&#34;C bar&#34;</span>) }  <span style="color:#75715e">// 与 D 类 的 bar 同名
</span><span style="color:#75715e"></span>
    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">D</span>.foo() {
        bar()         <span style="color:#75715e">// 调用 D.bar()，扩展接收者优先
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">this</span><span style="color:#a6e22e">@C</span>.bar()  <span style="color:#75715e">// 调用 C.bar()
</span><span style="color:#75715e"></span>    }

    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">caller</span>(d: D) {
        d.foo()   <span style="color:#75715e">// 调用扩展函数
</span><span style="color:#75715e"></span>    }
}

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> c: C = C()
    <span style="color:#66d9ef">val</span> d: D = D()
    c.caller(d)

}
</code></pre></div><p>实例执行输出结果为：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">D bar
C bar
</code></pre></div><p>以成员的形式定义的扩展函数, 可以声明为 open , 而且可以在子类中覆盖. 也就是说, 在这类扩展函数的派 发过程中, 针对分发接受者是虚拟的(virtual), 但针对扩展接受者仍然是静态的。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">open</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">D</span> {
}

<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">D1</span> : D() {
}

<span style="color:#66d9ef">open</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">C</span> {
    <span style="color:#66d9ef">open</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">D</span>.foo() {
        println(<span style="color:#e6db74">&#34;D.foo in C&#34;</span>)
    }

    <span style="color:#66d9ef">open</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">D1</span>.foo() {
        println(<span style="color:#e6db74">&#34;D1.foo in C&#34;</span>)
    }

    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">caller</span>(d: D) {
        d.foo()   <span style="color:#75715e">// 调用扩展函数
</span><span style="color:#75715e"></span>    }
}

<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">C1</span> : C() {
    <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">D</span>.foo() {
        println(<span style="color:#e6db74">&#34;D.foo in C1&#34;</span>)
    }

    <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">D1</span>.foo() {
        println(<span style="color:#e6db74">&#34;D1.foo in C1&#34;</span>)
    }
}


<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    C().caller(D())   <span style="color:#75715e">// 输出 &#34;D.foo in C&#34;
</span><span style="color:#75715e"></span>    C1().caller(D())  <span style="color:#75715e">// 输出 &#34;D.foo in C1&#34; —— 分发接收者虚拟解析
</span><span style="color:#75715e"></span>    C().caller(D1())  <span style="color:#75715e">// 输出 &#34;D.foo in C&#34; —— 扩展接收者静态解析
</span><span style="color:#75715e"></span>
}
</code></pre></div><p>实例执行输出结果为：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">D.foo <span style="color:#66d9ef">in</span> C
D.foo <span style="color:#66d9ef">in</span> C1
D.foo <span style="color:#66d9ef">in</span> C
</code></pre></div><p>伴生对象内的成员相当于 Java 中的静态成员，其生命周期伴随类始终，在伴生对象内部可以定义变量和函数，这些变量和函数可以直接用类名引用。</p>
<p>对于伴生对象扩展函数，有两种形式，一种是在类内扩展，一种是在类外扩展，这两种形式扩展后的函数互不影响(甚至名称都可以相同)，即使名称相同，它们也完全是两个不同的函数，并且有以下特点：</p>
<ul>
<li>(1)类内扩展的伴随对象函数和类外扩展的伴随对象可以同名，它们是两个独立的函数，互不影响；</li>
<li>(2)当类内扩展的伴随对象函数和类外扩展的伴随对象同名时，类内的其它函数优先引用类内扩展的伴随对象函数，即对于类内其它成员函数来说，类内扩展屏蔽类外扩展；</li>
<li>(3)类内扩展的伴随对象函数只能被类内的函数引用，不能被类外的函数和伴随对象内的函数引用；</li>
<li>(4)类外扩展的伴随对象函数可以被伴随对象内的函数引用，；</li>
</ul>
<p>例如以下代码：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyClass</span> {
    <span style="color:#66d9ef">companion</span> <span style="color:#66d9ef">object</span> {
        <span style="color:#66d9ef">val</span> myClassField1: Int = <span style="color:#ae81ff">1</span>
        <span style="color:#66d9ef">var</span> myClassField2 = <span style="color:#e6db74">&#34;this is myClassField2&#34;</span>
        <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">companionFun1</span>() {
            println(<span style="color:#e6db74">&#34;this is 1st companion function.&#34;</span>)
            foo()
        }
        <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">companionFun2</span>() {
            println(<span style="color:#e6db74">&#34;this is 2st companion function.&#34;</span>)
            companionFun1()
        }
    }
    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">MyClass</span>.Companion.foo() {
        println(<span style="color:#e6db74">&#34;伴随对象的扩展函数(内部)&#34;</span>)
    }
    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">test2</span>() {
        MyClass.foo()
    }
    <span style="color:#66d9ef">init</span> {
        test2()
    }
}
<span style="color:#66d9ef">val</span> MyClass.Companion.no: Int
    <span style="color:#66d9ef">get</span>() = <span style="color:#ae81ff">10</span>
<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">MyClass</span>.Companion.foo() {
    println(<span style="color:#e6db74">&#34;foo 伴随对象外部扩展函数&#34;</span>)
}
<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    println(<span style="color:#e6db74">&#34;no:</span><span style="color:#e6db74">${MyClass.no}</span><span style="color:#e6db74">&#34;</span>)
    println(<span style="color:#e6db74">&#34;field1:</span><span style="color:#e6db74">${MyClass.myClassField1}</span><span style="color:#e6db74">&#34;</span>)
    println(<span style="color:#e6db74">&#34;field2:</span><span style="color:#e6db74">${MyClass.myClassField2}</span><span style="color:#e6db74">&#34;</span>)
    MyClass.foo()
    MyClass.companionFun2()
}
</code></pre></div><p>运行结果：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">no:<span style="color:#ae81ff">10</span>
field1:<span style="color:#ae81ff">1</span>
field2:<span style="color:#66d9ef">this</span> <span style="color:#66d9ef">is</span> myClassField2
foo <span style="color:#960050;background-color:#1e0010">伴随对象外部扩展函数</span>
<span style="color:#66d9ef">this</span> <span style="color:#66d9ef">is</span> <span style="color:#ae81ff">2</span>st <span style="color:#66d9ef">companion</span> function.
<span style="color:#66d9ef">this</span> <span style="color:#66d9ef">is</span> <span style="color:#ae81ff">1</span>st <span style="color:#66d9ef">companion</span> function.
foo <span style="color:#960050;background-color:#1e0010">伴随对象外部扩展函数</span>
</code></pre></div><h1 id="kotlin-数据类与密封类">Kotlin 数据类与密封类</h1>
<hr>
<h2 id="数据类">数据类</h2>
<p>Kotlin 可以创建一个只包含数据的类，关键字为 <strong>data</strong>：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">data</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">User</span>(<span style="color:#66d9ef">val</span> name: String, <span style="color:#66d9ef">val</span> age: Int)
</code></pre></div><p>编译器会自动的从主构造函数中根据所有声明的属性提取以下函数：</p>
<ul>
<li><code>equals()</code> / <code>hashCode()</code></li>
<li><code>toString()</code> 格式如 <code>&quot;User(name=John, age=42)&quot;</code></li>
<li><code>componentN() functions</code> 对应于属性，按声明顺序排列</li>
<li><code>copy()</code> 函数</li>
</ul>
<p>如果这些函数在类中已经被明确定义了，或者从超类中继承而来，就不再会生成。</p>
<p>为了保证生成代码的一致性以及有意义，数据类需要满足以下条件：</p>
<ul>
<li>主构造函数至少包含一个参数。</li>
<li>所有的主构造函数的参数必须标识为<code>val</code> 或者 <code>var</code> ;</li>
<li>数据类不可以声明为 <code>abstract</code>, <code>open</code>, <code>sealed</code> 或者 <code>inner</code>;</li>
<li>数据类不能继承其他类 (但是可以实现接口)。</li>
</ul>
<h3 id="复制">复制</h3>
<p>复制使用 copy() 函数，我们可以使用该函数复制对象并修改部分属性, 对于上文的 User 类，其实现会类似下面这样：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">copy</span>(name: String = <span style="color:#66d9ef">this</span>.name, age: Int = <span style="color:#66d9ef">this</span>.age) = User(name, age)
</code></pre></div><h3 id="实例-11">实例</h3>
<p>使用 copy 类复制 User 数据类，并修改 age 属性:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">data</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">User</span>(<span style="color:#66d9ef">val</span> name: String, <span style="color:#66d9ef">val</span> age: Int)


<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">main</span>(args: Array&lt;String&gt;) {
    <span style="color:#66d9ef">val</span> jack = User(name = <span style="color:#e6db74">&#34;Jack&#34;</span>, age = <span style="color:#ae81ff">1</span>)
    <span style="color:#66d9ef">val</span> olderJack = jack.copy(age = <span style="color:#ae81ff">2</span>)
    println(jack)
    println(olderJack)

}
</code></pre></div><p>输出结果为：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">User(name=Jack, age=<span style="color:#ae81ff">1</span>)
User(name=Jack, age=<span style="color:#ae81ff">2</span>)
</code></pre></div><h3 id="数据类以及解构声明">数据类以及解构声明</h3>
<p>组件函数允许数据类在解构声明中使用：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">val</span> jane = User(<span style="color:#e6db74">&#34;Jane&#34;</span>, <span style="color:#ae81ff">35</span>)
<span style="color:#66d9ef">val</span> <span style="color:#960050;background-color:#1e0010">(</span>name, age) = jane
println(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">$name</span><span style="color:#e6db74">, </span><span style="color:#e6db74">$age</span><span style="color:#e6db74"> years of age&#34;</span>) <span style="color:#75715e">// prints &#34;Jane, 35 years of age&#34;
</span></code></pre></div><h3 id="标准数据类">标准数据类</h3>
<p>标准库提供了 <strong>Pair</strong> 和 <strong>Triple</strong> 。在大多数情形中，命名数据类是更好的设计选择，因为这样代码可读性更强而且提供了有意义的名字和属性。</p>
<hr>
<h2 id="密封类">密封类</h2>
<p>密封类用来表示受限的类继承结构：当一个值为有限几种的类型, 而不能有任何其他类型时。在某种意义上，他们是枚举类的扩展：枚举类型的值集合 也是受限的，但每个枚举常量只存在一个实例，而密封类 的一个子类可以有可包含状态的多个实例。</p>
<p>声明一个密封类，使用 <strong>sealed</strong> 修饰类，密封类可以有子类，但是所有的子类都必须要内嵌在密封类中。</p>
<p>sealed 不能修饰 interface ,abstract class(会报 warning,但是不会出现编译错误)</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">sealed</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Expr</span>
<span style="color:#66d9ef">data</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Const</span>(<span style="color:#66d9ef">val</span> number: Double) : Expr()
<span style="color:#66d9ef">data</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Sum</span>(<span style="color:#66d9ef">val</span> e1: Expr, <span style="color:#66d9ef">val</span> e2: Expr) : Expr()
<span style="color:#66d9ef">object</span> <span style="color:#a6e22e">NotANumber</span> : Expr()

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">eval</span>(expr: Expr): Double = <span style="color:#66d9ef">when</span> (expr) {
    <span style="color:#66d9ef">is</span> Const <span style="color:#f92672">-&gt;</span> expr.number
    <span style="color:#66d9ef">is</span> Sum <span style="color:#f92672">-&gt;</span> eval(expr.e1) + eval(expr.e2)
    NotANumber <span style="color:#f92672">-&gt;</span> Double.NaN
}
</code></pre></div><p>使用密封类的关键好处在于使用 when 表达式 的时候，如果能够 验证语句覆盖了所有情况，就不需要为该语句再添加一个 else 子句了。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">eval</span>(expr: Expr): Double = <span style="color:#66d9ef">when</span>(expr) {
    <span style="color:#66d9ef">is</span> Expr.Const <span style="color:#f92672">-&gt;</span> expr.number
    <span style="color:#66d9ef">is</span> Expr.Sum <span style="color:#f92672">-&gt;</span> eval(expr.e1) + eval(expr.e2)
    Expr.NotANumber <span style="color:#f92672">-&gt;</span> Double.NaN
    <span style="color:#75715e">// 不再需要 `else` 子句，因为我们已经覆盖了所有的情况
</span><span style="color:#75715e"></span>}
</code></pre></div><h2 id="笔记">笔记</h2>
<p>我的理解密封类就是一种专门用来配合 when 语句使用的类，举个例子，假如在 Android 中我们有一个 view，我们现在想通过 when 语句设置针对 view 进行两种操作：显示和隐藏，那么就可以这样做：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">sealed</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">UiOp</span> {
    <span style="color:#66d9ef">object</span> <span style="color:#a6e22e">Show</span>: UiOp()
    <span style="color:#66d9ef">object</span> <span style="color:#a6e22e">Hide</span>: UiOp()
} 
<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">execute</span>(view: View, op: UiOp) = <span style="color:#66d9ef">when</span> (op) {
    UiOp.Show <span style="color:#f92672">-&gt;</span> view.visibility = View.VISIBLE
    UiOp.Hide <span style="color:#f92672">-&gt;</span> view.visibility = View.GONE
}
</code></pre></div><p>以上功能其实完全可以用枚举实现，但是如果我们现在想加两个操作：水平平移和纵向平移，并且还要携带一些数据，比如平移了多少距离，平移过程的动画类型等数据，用枚举显然就不太好办了，这时密封类的优势就可以发挥了，例如：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">sealed</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">UiOp</span> {
    <span style="color:#66d9ef">object</span> <span style="color:#a6e22e">Show</span>: UiOp()
    <span style="color:#66d9ef">object</span> <span style="color:#a6e22e">Hide</span>: UiOp()
    <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">TranslateX</span>(<span style="color:#66d9ef">val</span> px: Float): UiOp()
    <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">TranslateY</span>(<span style="color:#66d9ef">val</span> px: Float): UiOp()
}

 

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">execute</span>(view: View, op: UiOp) = <span style="color:#66d9ef">when</span> (op) {
    UiOp.Show <span style="color:#f92672">-&gt;</span> view.visibility = View.VISIBLE
    UiOp.Hide <span style="color:#f92672">-&gt;</span> view.visibility = View.GONE
    <span style="color:#66d9ef">is</span> UiOp.TranslateX <span style="color:#f92672">-&gt;</span> view.translationX = op.px <span style="color:#75715e">// 这个 when 语句分支不仅告诉 view 要水平移动，还告诉 view 需要移动多少距离，这是枚举等 Java 传统思想不容易实现的
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">is</span> UiOp.TranslateY <span style="color:#f92672">-&gt;</span> view.translationY = op.px
}
</code></pre></div><p>以上代码中，TranslateX 是一个类，它可以携带多于一个的信息，比如除了告诉 view 需要水平平移之外，还可以告诉 view 平移多少像素，甚至还可以告诉 view 平移的动画类型等信息，我想这大概就是密封类出现的意义吧。</p>
<p>除此之外，如果 when 语句的分支不需要携带除“显示或隐藏view之外的其它信息”时(即只需要表明 when 语句分支，不需要携带额外数据时)，用 object 关键字创建单例就可以了，并且此时 when 子句不需要使用 is 关键字。只有需要携带额外信息时才定义密封类的子类，而且使用了密封类就不需要使用 else 子句，每当我们多增加一个密封类的子类或单例，编译器就会在 when 语句中给出提示，可以在编译阶段就及时发现错误，这也是以往 switch-case 语句和枚举不具备的功能。</p>
<p>最后，我们甚至可以把这一组操作封装成一个函数，以便日后调用，如下：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#75715e">// 先封装一个UI操作列表
</span><span style="color:#75715e"></span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Ui</span>(<span style="color:#66d9ef">val</span> uiOps: List = emptyList()) {
    <span style="color:#66d9ef">operator</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">plus</span>(uiOp: UiOp) = Ui(uiOps + uiOp)
}

<span style="color:#75715e">// 定义一组操作
</span><span style="color:#75715e"></span><span style="color:#66d9ef">val</span> ui = Ui() +
        UiOp.Show +
        UiOp.TranslateX(<span style="color:#ae81ff">20f</span>) +
        UiOp.TranslateY(<span style="color:#ae81ff">40f</span>) +
        UiOp.Hide
<span style="color:#75715e">// 定义调用的函数
</span><span style="color:#75715e"></span><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">run</span>(view: View, ui: Ui) {
    ui.uiOps.forEach { execute(view, <span style="color:#66d9ef">it</span>) }
}

run(view, ui) <span style="color:#75715e">// 最终调用
</span></code></pre></div><p>我的理解密封类就是一种专门用来配合 when 语句使用的类，举个例子，假如在 Android 中我们有一个 view，我们现在想通过 when 语句设置针对 view 进行两种操作：显示和隐藏，那么就可以这样做：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">sealed</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">UiOp</span> {
    <span style="color:#66d9ef">object</span> <span style="color:#a6e22e">Show</span>: UiOp()
    <span style="color:#66d9ef">object</span> <span style="color:#a6e22e">Hide</span>: UiOp()
} 
<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">execute</span>(view: View, op: UiOp) = <span style="color:#66d9ef">when</span> (op) {
    UiOp.Show <span style="color:#f92672">-&gt;</span> view.visibility = View.VISIBLE
    UiOp.Hide <span style="color:#f92672">-&gt;</span> view.visibility = View.GONE
}
</code></pre></div><p>以上功能其实完全可以用枚举实现，但是如果我们现在想加两个操作：水平平移和纵向平移，并且还要携带一些数据，比如平移了多少距离，平移过程的动画类型等数据，用枚举显然就不太好办了，这时密封类的优势就可以发挥了，例如：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">sealed</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">UiOp</span> {
    <span style="color:#66d9ef">object</span> <span style="color:#a6e22e">Show</span>: UiOp()
    <span style="color:#66d9ef">object</span> <span style="color:#a6e22e">Hide</span>: UiOp()
    <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">TranslateX</span>(<span style="color:#66d9ef">val</span> px: Float): UiOp()
    <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">TranslateY</span>(<span style="color:#66d9ef">val</span> px: Float): UiOp()
}

 

<span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">execute</span>(view: View, op: UiOp) = <span style="color:#66d9ef">when</span> (op) {
    UiOp.Show <span style="color:#f92672">-&gt;</span> view.visibility = View.VISIBLE
    UiOp.Hide <span style="color:#f92672">-&gt;</span> view.visibility = View.GONE
    <span style="color:#66d9ef">is</span> UiOp.TranslateX <span style="color:#f92672">-&gt;</span> view.translationX = op.px <span style="color:#75715e">// 这个 when 语句分支不仅告诉 view 要水平移动，还告诉 view 需要移动多少距离，这是枚举等 Java 传统思想不容易实现的
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">is</span> UiOp.TranslateY <span style="color:#f92672">-&gt;</span> view.translationY = op.px
}
</code></pre></div><p>以上代码中，TranslateX 是一个类，它可以携带多于一个的信息，比如除了告诉 view 需要水平平移之外，还可以告诉 view 平移多少像素，甚至还可以告诉 view 平移的动画类型等信息，我想这大概就是密封类出现的意义吧。</p>
<p>除此之外，如果 when 语句的分支不需要携带除“显示或隐藏view之外的其它信息”时(即只需要表明 when 语句分支，不需要携带额外数据时)，用 object 关键字创建单例就可以了，并且此时 when 子句不需要使用 is 关键字。只有需要携带额外信息时才定义密封类的子类，而且使用了密封类就不需要使用 else 子句，每当我们多增加一个密封类的子类或单例，编译器就会在 when 语句中给出提示，可以在编译阶段就及时发现错误，这也是以往 switch-case 语句和枚举不具备的功能。</p>
<p>最后，我们甚至可以把这一组操作封装成一个函数，以便日后调用，如下：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#75715e">// 先封装一个UI操作列表
</span><span style="color:#75715e"></span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Ui</span>(<span style="color:#66d9ef">val</span> uiOps: List = emptyList()) {
    <span style="color:#66d9ef">operator</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">plus</span>(uiOp: UiOp) = Ui(uiOps + uiOp)
}

<span style="color:#75715e">// 定义一组操作
</span><span style="color:#75715e"></span><span style="color:#66d9ef">val</span> ui = Ui() +
        UiOp.Show +
        UiOp.TranslateX(<span style="color:#ae81ff">20f</span>) +
        UiOp.TranslateY(<span style="color:#ae81ff">40f</span>) +
        UiOp.Hide
<span style="color:#75715e">// 定义调用的函数
</span><span style="color:#75715e"></span><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">run</span>(view: View, ui: Ui) {
    ui.uiOps.forEach { execute(view, <span style="color:#66d9ef">it</span>) }
}

run(view, ui) <span style="color:#75715e">// 最终调用
</span></code></pre></div><h1 id="kotlin-泛型">Kotlin 泛型</h1>
<p>泛型，即 &ldquo;参数化类型&rdquo;，将类型参数化，可以用在类，接口，方法上。</p>
<p>与 Java 一样，Kotlin 也提供泛型，为类型安全提供保证，消除类型强转的烦恼。</p>
<p>声明一个泛型类:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Box</span>&lt;T&gt;(t: T) {
    <span style="color:#66d9ef">var</span> value = t
}
</code></pre></div><p>创建类的实例时我们需要指定类型参数:</p>
<pre><code>val box: Box&lt;Int&gt; = Box&lt;Int&gt;(1)
// 或者
val box = Box(1) // 编译器会进行类型推断，1 类型 Int，所以编译器知道我们说的是 Box&lt;Int&gt;。
</code></pre><p>以下实例向泛型类 Box 传入整型数据和字符串：</p>
<pre><code>class Box&lt;T&gt;(t : T) {
    var value = t
}

fun main(args: Array&lt;String&gt;) {
    var boxInt = Box&lt;Int&gt;(10)
    var boxString = Box&lt;String&gt;(&quot;Breeze&quot;)

    println(boxInt.value)
    println(boxString.value)
}
</code></pre><p>输出结果为：</p>
<pre><code>10
Breeze
</code></pre><p>定义泛型类型变量，可以完整地写明类型参数，如果编译器可以自动推定类型参数，也可以省略类型参数。</p>
<p>Kotlin 泛型函数的声明与 Java 相同，类型参数要放在函数名的前面：</p>
<pre><code>fun &lt;T&gt; boxIn(value: T) = Box(value)

// 以下都是合法语句
val box4 = boxIn&lt;Int&gt;(1)
val box5 = boxIn(1)     // 编译器会进行类型推断
</code></pre><p>在调用泛型函数时，如果可以推断出类型参数，可以省略泛型参数。</p>
<p>以下实例创建了泛型函数 doPrintln，函数根据传入的不同类型做相应处理：</p>
<pre><code>fun main(args: Array&lt;String&gt;) {
    val age = 23
    val name = &quot;Breeze&quot;
    val bool = true

    doPrintln(age)    // 整型
    doPrintln(name)   // 字符串
    doPrintln(bool)   // 布尔型
}

fun &lt;T&gt; doPrintln(content: T) {

    when (content) {
        is Int -&gt; println(&quot;整型数字为 $content&quot;)
        is String -&gt; println(&quot;字符串转换为大写：${content.toUpperCase()}&quot;)
        else -&gt; println(&quot;T 不是整型，也不是字符串&quot;)
    }
}
</code></pre><p>输出结果为：</p>
<pre><code>整型数字为 23
字符串转换为大写：Breeze
T 不是整型，也不是字符串
</code></pre><hr>
<h2 id="泛型约束">泛型约束</h2>
<p>我们可以使用泛型约束来设定一个给定参数允许使用的类型。</p>
<p>Kotlin 中使用 : 对泛型的类型上限进行约束。</p>
<p>最常见的约束是上界(upper bound)：</p>
<pre><code>fun &lt;T : Comparable&lt;T&gt;&gt; sort(list: List&lt;T&gt;) {
    // ……
}
</code></pre><p>Comparable 的子类型可以替代 T。 例如:</p>
<pre><code>sort(listOf(1, 2, 3)) // OK。Int 是 Comparable&lt;Int&gt; 的子类型
sort(listOf(HashMap&lt;Int, String&gt;())) // 错误：HashMap&lt;Int, String&gt; 不是 Comparable&lt;HashMap&lt;Int, String&gt;&gt; 的子类型
</code></pre><p>默认的上界是 Any?。</p>
<p>对于多个上界约束条件，可以用 where 子句：</p>
<pre><code>fun &lt;T&gt; copyWhenGreater(list: List&lt;T&gt;, threshold: T): List&lt;String&gt;
    where T : CharSequence,
          T : Comparable&lt;T&gt; {
    return list.filter { it &gt; threshold }.map { it.toString() }
}
</code></pre><hr>
<h2 id="型变">型变</h2>
<p>Kotlin 中没有通配符类型，它有两个其他的东西：声明处型变(declaration-site variance)与类型投影(type projections)。</p>
<h3 id="声明处型变">声明处型变</h3>
<p>声明处的类型变异使用协变注解修饰符：in、out，消费者 in, 生产者 out。</p>
<p>使用 out 使得一个类型参数协变，协变类型参数只能用作输出，可以作为返回值类型但是无法作为入参的类型：</p>
<pre><code>// 定义一个支持协变的类
class Breeze&lt;out A&gt;(val a: A) {
    fun foo(): A {
        return a
    }
}

fun main(args: Array&lt;String&gt;) {
    var strCo: Breeze&lt;String&gt; = Breeze(&quot;a&quot;)
    var anyCo: Breeze&lt;Any&gt; = Breeze&lt;Any&gt;(&quot;b&quot;)
    anyCo = strCo
    println(anyCo.foo())   // 输出 a
}
</code></pre><p>in 使得一个类型参数逆变，逆变类型参数只能用作输入，可以作为入参的类型但是无法作为返回值的类型：</p>
<pre><code>// 定义一个支持逆变的类
class Breeze&lt;in A&gt;(a: A) {
    fun foo(a: A) {
    }
}

fun main(args: Array&lt;String&gt;) {
    var strDCo = Breeze(&quot;a&quot;)
    var anyDCo = Breeze&lt;Any&gt;(&quot;b&quot;)
    strDCo = anyDCo
}
</code></pre><hr>
<h2 id="星号投射">星号投射</h2>
<p>有些时候, 你可能想表示你并不知道类型参数的任何信息, 但是仍然希望能够安全地使用它. 这里所谓&quot;安全地使用&quot;是指, 对泛型类型定义一个类型投射, 要求这个泛型类型的所有的实体实例, 都是这个投射的子类型。</p>
<p>对于这个问题, Kotlin 提供了一种语法, 称为 星号投射(star-projection):</p>
<ul>
<li>假如类型定义为 Foo<!-- raw HTML omitted --> , 其中 T 是一个协变的类型参数, 上界(upper bound)为 TUpper ,Foo&lt;<em>&gt; 等价于 Foo<!-- raw HTML omitted --> . 它表示, 当 T 未知时, 你可以安全地从 Foo&lt;</em>&gt; 中 读取TUpper 类型的值.</li>
<li>假如类型定义为 Foo<!-- raw HTML omitted --> , 其中 T 是一个反向协变的类型参数, Foo&lt;<em>&gt; 等价于 Foo<!-- raw HTML omitted --> . 它表示, 当 T 未知时, 你不能安全地向 Foo&lt;</em>&gt; 写入 任何东西.</li>
<li>假如类型定义为 Foo<!-- raw HTML omitted --> , 其中 T 是一个协变的类型参数, 上界(upper bound)为 TUpper , 对于读取值的场合, Foo&lt;*&gt; 等价于 Foo<!-- raw HTML omitted --> , 对于写入值的场合, 等价于 Foo<!-- raw HTML omitted --> .</li>
</ul>
<p>如果一个泛型类型中存在多个类型参数, 那么每个类型参数都可以单独的投射. 比如, 如果类型定义为interface Function&lt;in T, out U&gt; , 那么可以出现以下几种星号投射:</p>
<ol>
<li>Function&lt;*, String&gt; , 代表 Function&lt;in Nothing, String&gt; ;</li>
<li>Function&lt;Int, *&gt; , 代表 Function&lt;Int, out Any?&gt; ;</li>
<li>Function&lt;<em>,</em> &gt; , 代表 Function&lt;in Nothing, out Any?&gt; .</li>
</ol>
<p>注意: 星号投射与 Java 的原生类型(raw type)非常类似, 但可以安全使用</p>
<p>关于星号投射，其实就是*代指了所有类型，相当于Any?</p>
<p>给文中补个例子方便理解：</p>
<pre><code>class A&lt;T&gt;(val t: T, val t2 : T, val t3 : T)
class Apple(var name : String)
fun main(args: Array&lt;String&gt;) {
    //使用类    
    val a1: A&lt;*&gt; = A(12, &quot;String&quot;, Apple(&quot;苹果&quot;))
    val a2: A&lt;Any?&gt; = A(12, &quot;String&quot;, Apple(&quot;苹果&quot;))   //和a1是一样的
    val apple = a1.t3    //参数类型为Any
    println(apple)
    val apple2 = apple as Apple   //强转成Apple类
    println(apple2.name)
    //使用数组
    val l:ArrayList&lt;*&gt; = arrayListOf(&quot;String&quot;,1,1.2f,Apple(&quot;苹果&quot;))
    for (item in l){
        println(item)
    }
}
</code></pre><h1 id="kotlin-枚举类">Kotlin 枚举类</h1>
<p>枚举类最基本的用法是实现一个类型安全的枚举。</p>
<p>枚举常量用逗号分隔,每个枚举常量都是一个对象。</p>
<pre><code>enum class Color{
    RED,BLACK,BLUE,GREEN,WHITE
}
</code></pre><h3 id="枚举初始化">枚举初始化</h3>
<p>每一个枚举都是枚举类的实例，它们可以被初始化：</p>
<pre><code>enum class Color(val rgb: Int) {
    RED(0xFF0000),
    GREEN(0x00FF00),
    BLUE(0x0000FF)
}
</code></pre><p>默认名称为枚举字符名，值从0开始。若需要指定值，则可以使用其构造函数：</p>
<pre><code>enum class Shape(value:Int){
    ovel(100),
    rectangle(200)
}
</code></pre><p>枚举还支持以声明自己的匿名类及相应的方法、以及覆盖基类的方法。如：</p>
<pre><code>enum class ProtocolState {
    WAITING {
        override fun signal() = TALKING
    },

    TALKING {
        override fun signal() = WAITING
    };

    abstract fun signal(): ProtocolState
}
</code></pre><p>如果枚举类定义任何成员，要使用分号将成员定义中的枚举常量定义分隔开</p>
<hr>
<h2 id="使用枚举常量">使用枚举常量</h2>
<p>Kotlin 中的枚举类具有合成方法，允许遍历定义的枚举常量，并通过其名称获取枚举常数。</p>
<pre><code>EnumClass.valueOf(value: String): EnumClass  // 转换指定 name 为枚举值，若未匹配成功，会抛出IllegalArgumentException
EnumClass.values(): Array&lt;EnumClass&gt;        // 以数组的形式，返回枚举值
</code></pre><p>获取枚举相关信息：</p>
<pre><code>val name: String //获取枚举名称
val ordinal: Int //获取枚举值在所有枚举数组中定义的顺序
</code></pre><h3 id="实例-12">实例</h3>
<pre><code>enum class Color{
    RED,BLACK,BLUE,GREEN,WHITE
}

fun main(args: Array&lt;String&gt;) {
    var color:Color=Color.BLUE

    println(Color.values())
    println(Color.valueOf(&quot;RED&quot;))
    println(color.name)
    println(color.ordinal)

}
</code></pre><p>自 Kotlin 1.1 起，可以使用 <code>enumValues&lt;T&gt;()</code> 和 <code>enumValueOf&lt;T&gt;()</code> 函数以泛型的方式访问枚举类中的常量 ：</p>
<pre><code>enum class RGB { RED, GREEN, BLUE }

inline fun &lt;reified T : Enum&lt;T&gt;&gt; printAllValues() {
    print(enumValues&lt;T&gt;().joinToString { it.name })
}



fun main(args: Array&lt;String&gt;) {
    printAllValues&lt;RGB&gt;() // 输出 RED, GREEN, BLUE
}
</code></pre><h1 id="kotlin-对象表达式和对象声明">Kotlin 对象表达式和对象声明</h1>
<p>Kotlin 用对象表达式和对象声明来实现创建一个对某个类做了轻微改动的类的对象，且不需要去声明一个新的子类。</p>
<hr>
<h2 id="对象表达式">对象表达式</h2>
<p>通过对象表达式实现一个匿名内部类的对象用于方法的参数中：</p>
<pre><code>window.addMouseListener(object : MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) {
        // ...
    }
    override fun mouseEntered(e: MouseEvent) {
        // ...
    }
})
</code></pre><p>对象可以继承于某个基类，或者实现其他接口:</p>
<pre><code>open class A(x: Int) {
    public open val y: Int = x
}

interface B {……}

val ab: A = object : A(1), B {
    override val y = 15
}
</code></pre><p>如果超类型有一个构造函数，则必须传递参数给它。多个超类型和接口可以用逗号分隔。</p>
<p>通过对象表达式可以越过类的定义直接得到一个对象：</p>
<pre><code>fun main(args: Array&lt;String&gt;) {
    val site = object {
        var name: String = &quot;菜鸟教程&quot;
        var url: String = &quot;www.Breeze.com&quot;
    }
    println(site.name)
    println(site.url)
}
</code></pre><p>请注意，匿名对象可以用作只在本地和私有作用域中声明的类型。如果你使用匿名对象作为公有函数的 返回类型或者用作公有属性的类型，那么该函数或属性的实际类型 会是匿名对象声明的超类型，如果你没有声明任何超类型，就会是 Any。在匿名对象 中添加的成员将无法访问。</p>
<pre><code>class C {
    // 私有函数，所以其返回类型是匿名对象类型
    private fun foo() = object {
        val x: String = &quot;x&quot;
    }

    // 公有函数，所以其返回类型是 Any
    fun publicFoo() = object {
        val x: String = &quot;x&quot;
    }

    fun bar() {
        val x1 = foo().x        // 没问题
        val x2 = publicFoo().x  // 错误：未能解析的引用“x”
    }
}
</code></pre><p>在对象表达中可以方便的访问到作用域中的其他变量:</p>
<pre><code>fun countClicks(window: JComponent) {
    var clickCount = 0
    var enterCount = 0

    window.addMouseListener(object : MouseAdapter() {
        override fun mouseClicked(e: MouseEvent) {
            clickCount++
        }

        override fun mouseEntered(e: MouseEvent) {
            enterCount++
        }
    })
    // ……
}
</code></pre><hr>
<h2 id="对象声明">对象声明</h2>
<p>Kotlin 使用 object 关键字来声明一个对象。</p>
<p>Kotlin 中我们可以方便的通过对象声明来获得一个单例。</p>
<pre><code>object DataProviderManager {
    fun registerDataProvider(provider: DataProvider) {
        // ……
    }

    val allDataProviders: Collection&lt;DataProvider&gt;
        get() = // ……
}
</code></pre><p>引用该对象，我们直接使用其名称即可：</p>
<pre><code>DataProviderManager.registerDataProvider(……)
</code></pre><p>当然你也可以定义一个变量来获取这个对象，当时当你定义两个不同的变量来获取这个对象时，你会发现你并不能得到两个不同的变量。也就是说通过这种方式，我们获得一个单例。</p>
<pre><code>var data1 = DataProviderManager
var data2 = DataProviderManager
data1.name = &quot;test&quot;
print(&quot;data1 name = ${data2.name}&quot;)  
</code></pre><h3 id="实例-13">实例</h3>
<p>以下实例中，两个对象都输出了同一个 url 地址：</p>
<pre><code>object Site {
    var url:String = &quot;&quot;
    val name: String = &quot;菜鸟教程&quot;
}
fun main(args: Array&lt;String&gt;) {
    var s1 =  Site
    var s2 = Site
    s1.url = &quot;www.Breeze.com&quot;
    println(s1.url)
    println(s2.url)
}
</code></pre><p>输出结果为:</p>
<pre><code>www.Breeze.com
www.Breeze.com
</code></pre><p>对象可以有超类型：</p>
<pre><code>object DefaultListener : MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) {
        // ……
    }

    override fun mouseEntered(e: MouseEvent) {
        // ……
    }
}
</code></pre><p>与对象表达式不同，当对象声明在另一个类的内部时，这个对象并不能通过外部类的实例访问到该对象，而只能通过类名来访问，同样该对象也不能直接访问到外部类的方法和变量。</p>
<pre><code>class Site {
    var name = &quot;菜鸟教程&quot;
    object DeskTop{
        var url = &quot;www.Breeze.com&quot;
        fun showName(){
            print{&quot;desk legs $name&quot;} // 错误，不能访问到外部类的方法和变量
        }
    }
}
fun main(args: Array&lt;String&gt;) {
    var site = Site()
    site.DeskTop.url // 错误，不能通过外部类的实例访问到该对象
    Site.DeskTop.url // 正确
}
</code></pre><hr>
<h2 id="伴生对象">伴生对象</h2>
<p>类内部的对象声明可以用 companion 关键字标记，这样它就与外部类关联在一起，我们就可以直接通过外部类访问到对象的内部元素。</p>
<pre><code>class MyClass {
    companion object Factory {
        fun create(): MyClass = MyClass()
    }
}

val instance = MyClass.create()   // 访问到对象的内部元素
</code></pre><p>我们可以省略掉该对象的对象名，然后使用 Companion 替代需要声明的对象名：</p>
<pre><code>class MyClass {
    companion object {
    }
}

val x = MyClass.Companion
</code></pre><p>**注意：**一个类里面只能声明一个内部关联对象，即关键字 companion 只能使用一次。</p>
<p>请伴生对象的成员看起来像其他语言的静态成员，但在运行时他们仍然是真实对象的实例成员。例如还可以实现接口：</p>
<pre><code>interface Factory&lt;T&gt; {
    fun create(): T
}


class MyClass {
    companion object : Factory&lt;MyClass&gt; {
        override fun create(): MyClass = MyClass()
    }
}
</code></pre><hr>
<h2 id="对象表达式和对象声明之间的语义差异">对象表达式和对象声明之间的语义差异</h2>
<p>对象表达式和对象声明之间有一个重要的语义差别：</p>
<ul>
<li>对象表达式是在使用他们的地方立即执行的</li>
<li>对象声明是在第一次被访问到时延迟初始化的</li>
<li>伴生对象的初始化是在相应的类被加载(解析)时，与 Java 静态初始化器的语义相匹配</li>
</ul>
<h1 id="kotlin-委托">kotlin 委托</h1>
<p>委托模式是软件设计模式中的一项基本技巧。在委托模式中，有两个对象参与处理同一个请求，接受请求的对象将请求委托给另一个对象来处理。</p>
<p>Kotlin 直接支持委托模式，更加优雅，简洁。Kotlin 通过关键字 by 实现委托。</p>
<hr>
<h2 id="类委托">类委托</h2>
<p>类的委托即一个类中定义的方法实际是调用另一个类的对象的方法来实现的。</p>
<p>以下实例中派生类 Derived 继承了接口 Base 所有方法，并且委托一个传入的 Base 类的对象来执行这些方法。</p>
<pre><code>// 创建接口
interface Base {   
    fun print()
}

// 实现此接口的被委托的类
class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

// 通过关键字 by 建立委托类
class Derived(b: Base) : Base by b

fun main(args: Array&lt;String&gt;) {
    val b = BaseImpl(10)
    Derived(b).print() // 输出 10
}
</code></pre><p>在 Derived 声明中，by 子句表示，将 b 保存在 Derived 的对象实例内部，而且编译器将会生成继承自 Base 接口的所有方法, 并将调用转发给 b。</p>
<hr>
<h2 id="属性委托">属性委托</h2>
<p>属性委托指的是一个类的某个属性值不是在类中直接进行定义，而是将其托付给一个代理类，从而实现对该类的属性统一管理。</p>
<p>属性委托语法格式：</p>
<pre><code>val/var &lt;属性名&gt;: &lt;类型&gt; by &lt;表达式&gt;
</code></pre><ul>
<li>var/val：属性类型(可变/只读)</li>
<li>属性名：属性名称</li>
<li>类型：属性的数据类型</li>
<li>表达式：委托代理类</li>
</ul>
<p>by 关键字之后的表达式就是委托, 属性的 get() 方法(以及set() 方法)将被委托给这个对象的 getValue() 和 setValue() 方法。属性委托不必实现任何接口, 但必须提供 getValue() 函数(对于 var属性,还需要 setValue() 函数)。</p>
<h3 id="定义一个被委托的类">定义一个被委托的类</h3>
<p>该类需要包含 getValue() 方法和 setValue() 方法，且参数 thisRef 为进行委托的类的对象，prop 为进行委托的属性的对象。</p>
<pre><code>import kotlin.reflect.KProperty
// 定义包含属性委托的类
class Example {
    var p: String by Delegate()
}

// 委托的类
class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty&lt;*&gt;): String {
        return &quot;$thisRef, 这里委托了 ${property.name} 属性&quot;
    }

    operator fun setValue(thisRef: Any?, property: KProperty&lt;*&gt;, value: String) {
        println(&quot;$thisRef 的 ${property.name} 属性赋值为 $value&quot;)
    }
}
fun main(args: Array&lt;String&gt;) {
    val e = Example()
    println(e.p)     // 访问该属性，调用 getValue() 函数

    e.p = &quot;Breeze&quot;   // 调用 setValue() 函数
    println(e.p)
}
</code></pre><p>输出结果为：</p>
<pre><code>Example@433c675d, 这里委托了 p 属性
Example@433c675d 的 p 属性赋值为 Breeze
Example@433c675d, 这里委托了 p 属性
</code></pre><hr>
<h2 id="标准委托">标准委托</h2>
<p>Kotlin 的标准库中已经内置了很多工厂方法来实现属性的委托。</p>
<h3 id="延迟属性-lazy">延迟属性 Lazy</h3>
<p>lazy() 是一个函数, 接受一个 Lambda 表达式作为参数, 返回一个 Lazy <!-- raw HTML omitted --> 实例的函数，返回的实例可以作为实现延迟属性的委托： 第一次调用 get() 会执行已传递给 lazy() 的 lamda 表达式并记录结果， 后续调用 get() 只是返回记录的结果。</p>
<pre><code>val lazyValue: String by lazy {
    println(&quot;computed!&quot;)     // 第一次调用输出，第二次调用不执行
    &quot;Hello&quot;
}

fun main(args: Array&lt;String&gt;) {
    println(lazyValue)   // 第一次执行，执行两次输出表达式
    println(lazyValue)   // 第二次执行，只输出返回值
}
</code></pre><p>执行输出结果：</p>
<pre><code>computed!
Hello
Hello
</code></pre><hr>
<h2 id="可观察属性-observable">可观察属性 Observable</h2>
<p>observable 可以用于实现观察者模式。</p>
<p>Delegates.observable() 函数接受两个参数: 第一个是初始化值, 第二个是属性值变化事件的响应器(handler)。</p>
<p>在属性赋值后会执行事件的响应器(handler)，它有三个参数：被赋值的属性、旧值和新值：</p>
<pre><code>import kotlin.properties.Delegates

class User {
    var name: String by Delegates.observable(&quot;初始值&quot;) {
        prop, old, new -&gt;
        println(&quot;旧值：$old -&gt; 新值：$new&quot;)
    }
}

fun main(args: Array&lt;String&gt;) {
    val user = User()
    user.name = &quot;第一次赋值&quot;
    user.name = &quot;第二次赋值&quot;
}
</code></pre><p>执行输出结果：</p>
<pre><code>旧值：初始值 -&gt; 新值：第一次赋值
旧值：第一次赋值 -&gt; 新值：第二次赋值
</code></pre><hr>
<h2 id="把属性储存在映射中">把属性储存在映射中</h2>
<p>一个常见的用例是在一个映射(map)里存储属性的值。 这经常出现在像解析 JSON 或者做其他&quot;动态&quot;事情的应用中。 在这种情况下，你可以使用映射实例自身作为委托来实现委托属性。</p>
<pre><code>class Site(val map: Map&lt;String, Any?&gt;) {
    val name: String by map
    val url: String  by map
}

fun main(args: Array&lt;String&gt;) {
    // 构造函数接受一个映射参数
    val site = Site(mapOf(
        &quot;name&quot; to &quot;菜鸟教程&quot;,
        &quot;url&quot;  to &quot;www.Breeze.com&quot;
    ))
    
    // 读取映射值
    println(site.name)
    println(site.url)
}
</code></pre><p>执行输出结果：</p>
<pre><code>菜鸟教程
www.Breeze.com
</code></pre><p>如果使用 var 属性，需要把 Map 换成 MutableMap：</p>
<pre><code>class Site(val map: MutableMap&lt;String, Any?&gt;) {
    val name: String by map
    val url: String by map
}

fun main(args: Array&lt;String&gt;) {

    var map:MutableMap&lt;String, Any?&gt; = mutableMapOf(
            &quot;name&quot; to &quot;菜鸟教程&quot;,
            &quot;url&quot; to &quot;www.Breeze.com&quot;
    )

    val site = Site(map)

    println(site.name)
    println(site.url)

    println(&quot;--------------&quot;)
    map.put(&quot;name&quot;, &quot;Google&quot;)
    map.put(&quot;url&quot;, &quot;www.google.com&quot;)

    println(site.name)
    println(site.url)

}
</code></pre><p>执行输出结果：</p>
<pre><code>菜鸟教程
www.Breeze.com
--------------
Google
www.google.com
</code></pre><hr>
<h2 id="not-null">Not Null</h2>
<p>notNull 适用于那些无法在初始化阶段就确定属性值的场合。</p>
<pre><code>class Foo {
    var notNullBar: String by Delegates.notNull&lt;String&gt;()
}

foo.notNullBar = &quot;bar&quot;
println(foo.notNullBar)
</code></pre><p>需要注意，如果属性在赋值前就被访问的话则会抛出异常。</p>
<hr>
<h2 id="局部委托属性">局部委托属性</h2>
<p>你可以将局部变量声明为委托属性。 例如，你可以使一个局部变量惰性初始化：</p>
<pre><code>fun example(computeFoo: () -&gt; Foo) {
    val memoizedFoo by lazy(computeFoo)

    if (someCondition &amp;&amp; memoizedFoo.isValid()) {
        memoizedFoo.doSomething()
    }
}
</code></pre><p>memoizedFoo 变量只会在第一次访问时计算。 如果 someCondition 失败，那么该变量根本不会计算。</p>
<hr>
<h2 id="属性委托要求">属性委托要求</h2>
<p>对于只读属性(也就是说val属性), 它的委托必须提供一个名为getValue()的函数。该函数接受以下参数：</p>
<ul>
<li>thisRef —— 必须与属性所有者类型(对于扩展属性——指被扩展的类型)相同或者是它的超类型</li>
<li>property —— 必须是类型 KProperty&lt;*&gt; 或其超类型</li>
</ul>
<p>这个函数必须返回与属性相同的类型(或其子类型)。</p>
<p>对于一个值可变(mutable)属性(也就是说,var 属性),除 getValue()函数之外,它的委托还必须 另外再提供一个名为setValue()的函数, 这个函数接受以下参数:</p>
<p>property —— 必须是类型 KProperty&lt;*&gt; 或其超类型new value —— 必须和属性同类型或者是它的超类型。</p>
<hr>
<h2 id="翻译规则">翻译规则</h2>
<p>在每个委托属性的实现的背后，Kotlin 编译器都会生成辅助属性并委托给它。 例如，对于属性 prop，生成隐藏属性 prop$delegate，而访问器的代码只是简单地委托给这个附加属性：</p>
<pre><code>class C {
    var prop: Type by MyDelegate()
}

// 这段是由编译器生成的相应代码：
class C {
    private val prop$delegate = MyDelegate()
    var prop: Type
        get() = prop$delegate.getValue(this, this::prop)
        set(value: Type) = prop$delegate.setValue(this, this::prop, value)
}
</code></pre><p>Kotlin 编译器在参数中提供了关于 prop 的所有必要信息：第一个参数 this 引用到外部类 C 的实例而 this::prop 是 KProperty 类型的反射对象，该对象描述 prop 自身。</p>
<hr>
<h2 id="提供委托">提供委托</h2>
<p>通过定义 provideDelegate 操作符，可以扩展创建属性实现所委托对象的逻辑。 如果 by 右侧所使用的对象将 provideDelegate 定义为成员或扩展函数，那么会调用该函数来 创建属性委托实例。</p>
<p>provideDelegate 的一个可能的使用场景是在创建属性时(而不仅在其 getter 或 setter 中)检查属性一致性。</p>
<p>例如，如果要在绑定之前检查属性名称，可以这样写：</p>
<pre><code>class ResourceLoader&lt;T&gt;(id: ResourceID&lt;T&gt;) {
    operator fun provideDelegate(
            thisRef: MyUI,
            prop: KProperty&lt;*&gt;
    ): ReadOnlyProperty&lt;MyUI, T&gt; {
        checkProperty(thisRef, prop.name)
        // 创建委托
    }

    private fun checkProperty(thisRef: MyUI, name: String) { …… }
}

fun &lt;T&gt; bindResource(id: ResourceID&lt;T&gt;): ResourceLoader&lt;T&gt; { …… }

class MyUI {
    val image by bindResource(ResourceID.image_id)
    val text by bindResource(ResourceID.text_id)
}
</code></pre><p>provideDelegate 的参数与 getValue 相同：</p>
<ul>
<li>thisRef —— 必须与 属性所有者 类型(对于扩展属性——指被扩展的类型)相同或者是它的超类型</li>
<li>property —— 必须是类型 KProperty&lt;*&gt; 或其超类型。</li>
</ul>
<p>在创建 MyUI 实例期间，为每个属性调用 provideDelegate 方法，并立即执行必要的验证。</p>
<p>如果没有这种拦截属性与其委托之间的绑定的能力，为了实现相同的功能， 你必须显式传递属性名，这不是很方便：</p>
<pre><code>// 检查属性名称而不使用“provideDelegate”功能
class MyUI {
    val image by bindResource(ResourceID.image_id, &quot;image&quot;)
    val text by bindResource(ResourceID.text_id, &quot;text&quot;)
}

fun &lt;T&gt; MyUI.bindResource(
        id: ResourceID&lt;T&gt;,
        propertyName: String
): ReadOnlyProperty&lt;MyUI, T&gt; {
   checkProperty(this, propertyName)
   // 创建委托
}
</code></pre><p>在生成的代码中，会调用 provideDelegate 方法来初始化辅助的 prop$delegate 属性。 比较对于属性声明 val prop: Type by MyDelegate() 生成的代码与 上面(当 provideDelegate 方法不存在时)生成的代码：</p>
<pre><code>class C {
    var prop: Type by MyDelegate()
}

// 这段代码是当“provideDelegate”功能可用时
// 由编译器生成的代码：
class C {
    // 调用“provideDelegate”来创建额外的“delegate”属性
    private val prop$delegate = MyDelegate().provideDelegate(this, this::prop)
    val prop: Type
        get() = prop$delegate.getValue(this, this::prop)
}
</code></pre><p>请注意，provideDelegate 方法只影响辅助属性的创建，并不会影响为 getter 或 setter 生成的代码。</p>
        </div>

        
<div class="post-archive">
    <ul class="post-copyright">
        <li><strong>原文作者：</strong><a rel="author" href="https://luckly.work/">luckly</a></li>
        <li style="word-break:break-all"><strong>原文链接：</strong><a href="https://luckly.work/post/kotlin/kotlin%E6%9E%81%E9%80%9F%E5%85%A5%E9%97%A8/">https://luckly.work/post/kotlin/kotlin%E6%9E%81%E9%80%9F%E5%85%A5%E9%97%A8/</a></li>
        <li><strong>版权声明：</strong>本作品采用<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议</a>进行许可，非商业转载请注明出处（作者，原文链接），商业转载请联系作者获得授权。</li>
    </ul>
</div>
<br/>



        

<div class="post-archive">
    <h2>See Also</h2>
    <ul class="listing">
        
        <li><a href="/post/%E4%BF%A1%E6%81%AF/%E7%BB%84%E5%90%88%E7%AE%A1%E7%90%86%E9%87%8F%E5%8C%96%E7%9A%84%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/">组合管理、量化的项目管理</a></li>
        
        <li><a href="/post/flutter_tips/flutter%E5%9C%B0%E5%9D%80%E9%80%89%E6%8B%A9/">Flutter地址选择</a></li>
        
        <li><a href="/post/flutter_widget/flutter%E7%BB%84%E4%BB%B6OverFlowBoxSizedOverFlowBox/">Flutter组件OverFlowBox,SizedOverFlowBox</a></li>
        
        <li><a href="/post/%E4%BF%A1%E6%81%AF/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86%E6%88%90%E7%86%9F%E5%BA%A6%E6%A8%A1%E5%9E%8B%E7%9F%A5%E8%AF%86%E4%BA%A7%E6%9D%83%E6%A0%87%E5%87%86%E8%A7%84%E8%8C%83%E6%94%B6%E5%B0%BE%E7%AE%A1%E7%90%86/">项目管理成熟度模型、知识产权、标准规范、收尾管理</a></li>
        
        <li><a href="/post/%E4%BF%A1%E6%81%AF/%E6%88%90%E6%9C%AC%E7%AE%A1%E7%90%86/">成本管理</a></li>
        
    </ul>
</div>


        <div class="post-meta meta-tags">
            
            <ul class="clearfix">
                
                <li><a href='/tags/Kotlin'>Kotlin</a></li>
                
            </ul>
            
        </div>
    </article>
    
    

    
    
    <div class="post bg-white">
      <script src="https://utteranc.es/client.js"
            repo= "https://github.com/ITmxs/repo"
            issue-term="pathname"
            theme="github-light"
            crossorigin="anonymous"
            async>
      </script>
    </div>
    
</div>

                    <footer id="footer">
    <div>
        &copy; 2021 <a href="https://luckly.work/">早起的年轻人 By luckly</a>
        
        | <a rel="nofollow" target="_blank" href="http://beian.miit.gov.cn/">粤ICP备2021号-1</a>
        
    </div>
    <br />
    <div>
        <div class="github-badge">
            <a href="https://juejin.cn/user/3843548384077192" target="_black" rel="nofollow"><span class="badge-subject">Powered by</span><span class="badge-value bg-blue">掘金</span></a>
        </div>
        <div class="github-badge">
            <a href="https://space.bilibili.com/480883651" target="_black"><span class="badge-subject">Design by</span><span class="badge-value bg-brightgreen">早起的年轻人</span></a>
        </div>
        <div class="github-badge">
            <a href="https://cloud.tencent.com/developer/user/6702670" target="_black"><span class="badge-subject">Theme</span><span class="badge-value bg-yellowgreen">云社区</span></a>
        </div>
    </div>
</footer>


    
    <script type="text/javascript">
        window.MathJax = {
            tex2jax: {
                inlineMath: [['$', '$']],
                processEscapes: true
                }
            };
    </script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML' async></script><script src="https://cdn.bootcdn.net/ajax/libs/fancybox/3.5.7/jquery.fancybox.min.js"></script>

<a id="rocket" href="#top"></a>
<script type="text/javascript" src='/js/totop.js?v=0.0.0' async=""></script>



    <script type="text/javascript" src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" async></script>




    <script src='/js/douban.js'></script>
    <script src="/js/copy-to-clipboard.js"></script>

                </div>

                <div id="secondary">
    <section class="widget">
        <form id="search" action='https://luckly.work/search/' method="get" accept-charset="utf-8" target="_blank" _lpchecked="1">
      
      <input type="text" name="q" maxlength="20" placeholder="Search">
      <input type="hidden" name="sitesearch" value="https://luckly.work/">
      <button type="submit" class="submit icon-search"></button>
</form>
    </section>
    
    <section class="widget">
        <h3 class="widget-title">最近文章</h3>
<ul class="widget-list">
    
    <li>
        <a href="https://luckly.work/post/nginx/nginx%E6%96%87%E4%BB%B6%E5%86%85%E5%AE%B9/" title="Nginx文件内容">Nginx文件内容</a>
    </li>
    
    <li>
        <a href="https://luckly.work/post/nginx/nginx%E6%9E%81%E7%AE%80%E6%95%99%E7%A8%8B/" title="Nginx极简教程">Nginx极简教程</a>
    </li>
    
    <li>
        <a href="https://luckly.work/post/nginx/%E5%8D%81%E5%88%86%E9%92%9F%E5%85%A5%E9%97%A8nginx/" title="十分钟入门Nginx">十分钟入门Nginx</a>
    </li>
    
    <li>
        <a href="https://luckly.work/post/go/Goland%E8%BF%9C%E7%A8%8B%E5%BC%80%E5%8F%91%E9%85%8D%E7%BD%AE/" title="Goland远程开发配置">Goland远程开发配置</a>
    </li>
    
    <li>
        <a href="https://luckly.work/post/%E8%AF%BB%E4%B9%A6/%E5%9F%9F%E5%90%8D%E8%A7%A3%E6%9E%90/" title="域名解析">域名解析</a>
    </li>
    
    <li>
        <a href="https://luckly.work/post/git/%E6%8F%90%E4%BA%A4%E8%BF%87%E7%A8%8B%E7%9C%81%E7%95%A5%E6%9F%90%E4%BA%9B%E6%96%87%E4%BB%B6/" title="提交过程省略某些文件">提交过程省略某些文件</a>
    </li>
    
    <li>
        <a href="https://luckly.work/post/flutter_tips/Flutter_DropdownButton%E7%A4%BA%E4%BE%8B/" title="Flutter_DropdownButton示例">Flutter_DropdownButton示例</a>
    </li>
    
    <li>
        <a href="https://luckly.work/post/flutter_tips/Flutter_ExpansionPanelList%E5%92%8CExpansionPanelList.radio%E7%A4%BA%E4%BE%8B/" title="Flutter_ExpansionPanelList和ExpansionPanelList">Flutter_ExpansionPanelList和ExpansionPanelList</a>
    </li>
    
    <li>
        <a href="https://luckly.work/post/flutter_tips/Flutter%E5%BE%AE%E4%BF%A1%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5%E8%B7%B3%E5%9B%9EApp%E6%8C%87%E5%AE%9A%E9%A1%B5%E9%9D%A2/" title="Flutter微信分享链接跳回App指定页面">Flutter微信分享链接跳回App指定页面</a>
    </li>
    
    <li>
        <a href="https://luckly.work/post/%E4%BF%A1%E6%81%AF%E7%B3%BB%E7%BB%9F/%E5%A4%96%E5%8C%85%E5%8F%AF%E8%83%BD%E7%9A%84%E9%97%AE%E9%A2%98/" title="外包可能的问题">外包可能的问题</a>
    </li>
    
</ul>
    </section>

    

    <section class="widget">
        <h3 class="widget-title"><a href='/categories/'>分类</a></h3>
<ul class="widget-list">
    
    <li><a href="https://luckly.work/categories/Flutter/">Flutter (326)</a></li>
    
    <li><a href="https://luckly.work/categories/IT/">IT (2)</a></li>
    
    <li><a href="https://luckly.work/categories/Kotlin/">Kotlin (2)</a></li>
    
    <li><a href="https://luckly.work/categories/Mysql/">Mysql (1)</a></li>
    
    <li><a href="https://luckly.work/categories/nginx/">nginx (1)</a></li>
    
    <li><a href="https://luckly.work/categories/Vue/">Vue (6)</a></li>
    
    <li><a href="https://luckly.work/categories/YouTube%E8%A7%86%E9%A2%91%E4%B8%8B%E8%BD%BD/">YouTube视频下载 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/android/">android (6)</a></li>
    
    <li><a href="https://luckly.work/categories/dart/">dart (96)</a></li>
    
    <li><a href="https://luckly.work/categories/Flutter/">Flutter (28)</a></li>
    
    <li><a href="https://luckly.work/categories/gin/">gin (25)</a></li>
    
    <li><a href="https://luckly.work/categories/git/">git (4)</a></li>
    
    <li><a href="https://luckly.work/categories/Go/">Go (102)</a></li>
    
    <li><a href="https://luckly.work/categories/gorm/">gorm (4)</a></li>
    
    <li><a href="https://luckly.work/categories/grpc/">grpc (1)</a></li>
    
    <li><a href="https://luckly.work/categories/html/">html (3)</a></li>
    
    <li><a href="https://luckly.work/categories/ios/">ios (1)</a></li>
    
    <li><a href="https://luckly.work/categories/linux/">linux (1)</a></li>
    
    <li><a href="https://luckly.work/categories/nginx/">nginx (6)</a></li>
    
    <li><a href="https://luckly.work/categories/python/">python (35)</a></li>
    
    <li><a href="https://luckly.work/categories/read/">读书笔记 (6)</a></li>
    
    <li><a href="https://luckly.work/categories/redis/">redis (2)</a></li>
    
    <li><a href="https://luckly.work/categories/%E4%B8%AA%E4%BA%BA%E8%B5%84%E6%96%99/">个人资料 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/%E4%B9%A6%E5%8D%95/">书单 (8)</a></li>
    
    <li><a href="https://luckly.work/categories/%E4%B9%A6%E8%AF%84/">书评 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/%E4%BF%A1%E6%81%AF%E7%B3%BB%E7%BB%9F%E7%AE%A1%E7%90%86%E5%B8%88/">信息系统管理师 (19)</a></li>
    
    <li><a href="https://luckly.work/categories/%E4%BF%A1%E6%81%AF%E7%B3%BB%E7%BB%9F%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86%E5%B8%88/">信息系统项目管理师 (25)</a></li>
    
    <li><a href="https://luckly.work/categories/%E5%8E%9F%E5%88%99/">原则 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/%E5%8E%9F%E7%94%9F%E9%80%9A%E8%AE%AF/">原生通讯 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/%E5%9F%BA%E7%A1%80/">基础 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/%E5%A4%8D%E5%88%A9%E6%95%88%E5%BA%94/">复利效应 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B/">安装教程 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/%E5%B0%91%E6%9C%89%E4%BA%BA%E8%B5%B0%E7%9A%84%E8%B7%AF/">少有人走的路 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/%E5%BF%83%E8%AF%AD/">心语 (3)</a></li>
    
    <li><a href="https://luckly.work/categories/%E6%8F%92%E4%BB%B6/">插件 (2)</a></li>
    
    <li><a href="https://luckly.work/categories/%E6%95%99%E5%AD%A6/">教学 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/%E7%8E%8B%E9%98%B3%E6%98%8E/">王阳明 (3)</a></li>
    
    <li><a href="https://luckly.work/categories/%E7%94%B5%E5%AD%90%E4%B9%A6/">电子书 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/%E7%99%BB%E9%99%86%E8%A1%A8%E5%8D%95/">登陆表单 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/%E7%A8%BB%E7%9B%9B%E5%92%8C%E5%A4%AB/">稻盛和夫 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/%E7%A9%B7%E7%88%B8%E7%88%B8%E5%AF%8C%E7%88%B8%E7%88%B8/">穷爸爸富爸爸 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/%E7%B2%BE%E8%BF%9B/">精进 (3)</a></li>
    
    <li><a href="https://luckly.work/categories/%E7%BC%96%E7%A8%8B/">编程 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/%E8%99%9A%E5%B9%BB/">虚幻 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/%E8%B4%A2%E5%8A%A1%E8%87%AA%E7%94%B1%E4%B9%8B%E8%B7%AF/">财务自由之路 (2)</a></li>
    
    <li><a href="https://luckly.work/categories/%E8%B7%91%E6%AD%A5/">跑步 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/%E8%B7%AF%E7%94%B1%E4%BC%A0%E5%8F%82/">路由传参 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/%E8%B7%AF%E7%BA%BF/">路线 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/%E8%BD%AF%E4%BB%B6%E5%AE%9E%E6%96%BD/">软件实施 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/%E9%98%B3%E6%98%8E%E5%BF%83%E5%AD%A6/">阳明心学 (3)</a></li>
    
    <li><a href="https://luckly.work/categories/%E9%A1%B9%E7%9B%AE/">项目 (1)</a></li>
    
    <li><a href="https://luckly.work/categories/%E9%AD%85%E5%8A%9B/">魅力 (1)</a></li>
    
</ul>
    </section>

    <section class="widget">
        <h3 class="widget-title"><a href='/tags/'>标签</a></h3>
<div class="tagcloud">
    
    <a href="https://luckly.work/tags/flutter/">flutter</a>
    
    <a href="https://luckly.work/tags/IT/">IT</a>
    
    <a href="https://luckly.work/tags/Kotlin/">Kotlin</a>
    
    <a href="https://luckly.work/tags/Mysql/">Mysql</a>
    
    <a href="https://luckly.work/tags/nginx/">nginx</a>
    
    <a href="https://luckly.work/tags/Vue/">Vue</a>
    
    <a href="https://luckly.work/tags/YouTube%E8%A7%86%E9%A2%91%E4%B8%8B%E8%BD%BD/">YouTube视频下载</a>
    
    <a href="https://luckly.work/tags/android/">android</a>
    
    <a href="https://luckly.work/tags/dart/">dart</a>
    
    <a href="https://luckly.work/tags/flutter/">flutter</a>
    
    <a href="https://luckly.work/tags/gin/">gin</a>
    
    <a href="https://luckly.work/tags/git/">git</a>
    
    <a href="https://luckly.work/tags/go/">go</a>
    
    <a href="https://luckly.work/tags/gorm/">gorm</a>
    
    <a href="https://luckly.work/tags/grpc/">grpc</a>
    
    <a href="https://luckly.work/tags/html/">html</a>
    
    <a href="https://luckly.work/tags/ios/">ios</a>
    
    <a href="https://luckly.work/tags/linux/">linux</a>
    
    <a href="https://luckly.work/tags/nginx/">nginx</a>
    
    <a href="https://luckly.work/tags/python/">python</a>
    
    <a href="https://luckly.work/tags/redis/">redis</a>
    
    <a href="https://luckly.work/tags/%E4%B8%AA%E4%BA%BA%E8%B5%84%E6%96%99/">个人资料</a>
    
    <a href="https://luckly.work/tags/%E4%B9%A6%E5%8D%95/">书单</a>
    
    <a href="https://luckly.work/tags/%E4%B9%A6%E8%AF%84/">书评</a>
    
    <a href="https://luckly.work/tags/%E4%BF%A1%E6%81%AF%E7%B3%BB%E7%BB%9F%E7%AE%A1%E7%90%86%E5%B8%88/">信息系统管理师</a>
    
    <a href="https://luckly.work/tags/%E4%BF%A1%E6%81%AF%E7%B3%BB%E7%BB%9F%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86%E5%B8%88/">信息系统项目管理师</a>
    
    <a href="https://luckly.work/tags/%E5%85%A5%E9%97%A8/">入门</a>
    
    <a href="https://luckly.work/tags/%E5%8E%9F%E5%88%99/">原则</a>
    
    <a href="https://luckly.work/tags/%E5%8E%9F%E7%94%9F%E9%80%9A%E8%AE%AF/">原生通讯</a>
    
    <a href="https://luckly.work/tags/%E5%9F%BA%E7%A1%80/">基础</a>
    
    <a href="https://luckly.work/tags/%E5%A4%8D%E5%88%A9%E6%95%88%E5%BA%94/">复利效应</a>
    
    <a href="https://luckly.work/tags/%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B/">安装教程</a>
    
    <a href="https://luckly.work/tags/%E5%B0%91%E6%9C%89%E4%BA%BA%E8%B5%B0%E7%9A%84%E8%B7%AF/">少有人走的路</a>
    
    <a href="https://luckly.work/tags/%E5%BF%83%E8%AF%AD/">心语</a>
    
    <a href="https://luckly.work/tags/%E6%8F%92%E4%BB%B6/">插件</a>
    
    <a href="https://luckly.work/tags/%E6%95%99%E5%AD%A6/">教学</a>
    
    <a href="https://luckly.work/tags/%E7%8E%8B%E9%98%B3%E6%98%8E/">王阳明</a>
    
    <a href="https://luckly.work/tags/%E7%94%B5%E5%AD%90%E4%B9%A6/">电子书</a>
    
    <a href="https://luckly.work/tags/%E7%99%BB%E9%99%86%E8%A1%A8%E5%8D%95/">登陆表单</a>
    
    <a href="https://luckly.work/tags/%E7%A8%BB%E7%9B%9B%E5%92%8C%E5%A4%AB/">稻盛和夫</a>
    
    <a href="https://luckly.work/tags/%E7%A9%B7%E7%88%B8%E7%88%B8%E5%AF%8C%E7%88%B8%E7%88%B8/">穷爸爸富爸爸</a>
    
    <a href="https://luckly.work/tags/%E7%B2%BE%E8%BF%9B/">精进</a>
    
    <a href="https://luckly.work/tags/%E7%BC%96%E7%A8%8B/">编程</a>
    
    <a href="https://luckly.work/tags/%E8%99%9A%E5%B9%BB/">虚幻</a>
    
    <a href="https://luckly.work/tags/%E8%AF%97/">诗</a>
    
    <a href="https://luckly.work/tags/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/">读书笔记</a>
    
    <a href="https://luckly.work/tags/%E8%B4%A2%E5%8A%A1%E8%87%AA%E7%94%B1%E4%B9%8B%E8%B7%AF/">财务自由之路</a>
    
    <a href="https://luckly.work/tags/%E8%B7%91%E6%AD%A5/">跑步</a>
    
    <a href="https://luckly.work/tags/%E8%B7%AF%E7%94%B1%E4%BC%A0%E5%8F%82/">路由传参</a>
    
    <a href="https://luckly.work/tags/%E8%B7%AF%E7%BA%BF/">路线</a>
    
    <a href="https://luckly.work/tags/%E8%BD%AF%E4%BB%B6%E5%AE%9E%E6%96%BD/">软件实施</a>
    
    <a href="https://luckly.work/tags/%E9%80%9A%E8%AE%AF%E5%BD%95/">通讯录</a>
    
    <a href="https://luckly.work/tags/%E9%98%B3%E6%98%8E%E5%BF%83%E5%AD%A6/">阳明心学</a>
    
    <a href="https://luckly.work/tags/%E9%A1%B9%E7%9B%AE/">项目</a>
    
    <a href="https://luckly.work/tags/%E9%AD%85%E5%8A%9B/">魅力</a>
    
</div>
    </section>

    
<section class="widget">
    <h3 class="widget-title">友情链接</h3>
    <ul class="widget-list">
        
        <li>
            <a target="_blank" href="http://www.topgoer.com/" title="枯藤">枯藤</a>
        </li>
        
        <li>
            <a target="_blank" href="https://gorm.cn/zh_CN/docs/index.html" title="gorm">gorm</a>
        </li>
        
        <li>
            <a target="_blank" href="https://docs.python.org/zh-cn/3/tutorial/index.html" title="python">python</a>
        </li>
        
        <li>
            <a target="_blank" href="https://www.liwenzhou.com/" title="李文周">李文周的博客</a>
        </li>
        
        <li>
            <a target="_blank" href="http://www.xbzweb.com/" title="小包子的博客">小包子的博客</a>
        </li>
        
        <li>
            <a target="_blank" href="https://www.flysnow.org/" title="飞雪无情的博客">飞雪无情的博客</a>
        </li>
        
        <li>
            <a target="_blank" href="https://sliverhorn.com/" title="sliverhorn的博客">sliverhorn的博客</a>
        </li>
        
        <li>
            <a target="_blank" href="http://yuedu.baidu.com/ebook/14a722970740be1e640e9a3e" title="Android Gradle权威指南">Android Gradle权威指南</a>
        </li>
        
        <li>
            <a target="_blank" href="https://gesdh.cn/" title="小格子">格子导航</a>
        </li>
        
        <li>
            <a target="_blank" href="https://itachi.xyz/" title="阿林">itachi&#39;s Blog</a>
        </li>
        
        <li>
            <a target="_blank" href="https://darjun.github.io/" title="大俊">大俊Blog</a>
        </li>
        
        <li>
            <a target="_blank" href="https://geektutu.com/post/quick-golang.html" title="极客兔兔">极客兔兔Blog</a>
        </li>
        
        <li>
            <a target="_blank" href="http://zxfcumtcs.github.io/" title="赵雪峰">雪峰Blog</a>
        </li>
        
    </ul>
</section>


    <section class="widget">
        <h3 class="widget-title">其它</h3>
        <ul class="widget-list">
            <li><a href="https://luckly.work/index.xml">文章 RSS</a></li>
        </ul>
    </section>
</div>
            </div>
        </div>
    </div>
</body>

</html>